@angular-helpers/browser-web-apis 21.2.0 → 21.4.0
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.
- package/README.es.md +105 -0
- package/README.md +190 -0
- package/fesm2022/angular-helpers-browser-web-apis.mjs +1123 -140
- package/package.json +9 -1
- package/types/angular-helpers-browser-web-apis.d.ts +576 -54
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { Injectable, inject, DestroyRef, PLATFORM_ID, signal, computed, ElementRef, makeEnvironmentProviders } from '@angular/core';
|
|
2
|
+
import { Injectable, inject, DestroyRef, PLATFORM_ID, signal, computed, isSignal, effect, ElementRef, makeEnvironmentProviders } from '@angular/core';
|
|
3
3
|
import { isPlatformBrowser, isPlatformServer } from '@angular/common';
|
|
4
4
|
import { Observable, fromEvent, Subject, of, map as map$1 } from 'rxjs';
|
|
5
5
|
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
|
|
@@ -30,48 +30,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
30
30
|
}] });
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
* Base class for all Browser Web API services
|
|
33
|
+
* Base class for all Browser Web API services.
|
|
34
34
|
* Provides common functionality for:
|
|
35
|
-
* -
|
|
36
|
-
* -
|
|
37
|
-
* - Error
|
|
35
|
+
* - Platform detection (browser vs server)
|
|
36
|
+
* - Support assertion via Template Method
|
|
37
|
+
* - Error creation with cause chaining
|
|
38
|
+
* - Structured logging
|
|
38
39
|
* - Lifecycle management with destroyRef
|
|
39
|
-
*
|
|
40
|
+
*
|
|
41
|
+
* Services that also need permission querying should extend
|
|
42
|
+
* `PermissionAwareBrowserApiBaseService` instead.
|
|
40
43
|
*/
|
|
41
44
|
class BrowserApiBaseService {
|
|
42
|
-
permissionsService = inject(PermissionsService);
|
|
43
45
|
destroyRef = inject(DestroyRef);
|
|
44
46
|
platformId = inject(PLATFORM_ID);
|
|
45
47
|
/**
|
|
46
|
-
* Check if running in browser environment using Angular's platform detection
|
|
48
|
+
* Check if running in browser environment using Angular's platform detection.
|
|
47
49
|
*/
|
|
48
50
|
isBrowserEnvironment() {
|
|
49
51
|
return isPlatformBrowser(this.platformId);
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
52
|
-
* Check if running in server environment using Angular's platform detection
|
|
54
|
+
* Check if running in server environment using Angular's platform detection.
|
|
53
55
|
*/
|
|
54
56
|
isServerEnvironment() {
|
|
55
57
|
return isPlatformServer(this.platformId);
|
|
56
58
|
}
|
|
57
59
|
/**
|
|
58
|
-
*
|
|
60
|
+
* Template Method: throws a standard error when the API is not available.
|
|
61
|
+
* Uses getApiName() to produce consistent error messages.
|
|
59
62
|
*/
|
|
60
|
-
|
|
61
|
-
if (this.
|
|
63
|
+
ensureSupported() {
|
|
64
|
+
if (!this.isBrowserEnvironment()) {
|
|
62
65
|
throw new Error(`${this.getApiName()} API not available in server environment`);
|
|
63
66
|
}
|
|
64
|
-
try {
|
|
65
|
-
const status = await this.permissionsService.query({ name: permission });
|
|
66
|
-
return status.state === 'granted';
|
|
67
|
-
}
|
|
68
|
-
catch (error) {
|
|
69
|
-
console.error(`[${this.getApiName()}] Error requesting permission for ${permission}:`, error);
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
67
|
}
|
|
73
68
|
/**
|
|
74
|
-
* Create an error with proper cause chaining
|
|
69
|
+
* Create an error with proper cause chaining.
|
|
75
70
|
*/
|
|
76
71
|
createError(message, cause) {
|
|
77
72
|
const error = new Error(message);
|
|
@@ -80,6 +75,18 @@ class BrowserApiBaseService {
|
|
|
80
75
|
}
|
|
81
76
|
return error;
|
|
82
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Log an error with the service name as prefix.
|
|
80
|
+
*/
|
|
81
|
+
logError(message, error) {
|
|
82
|
+
console.error(`[${this.getApiName()}] ${message}`, error);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Log an informational message with the service name as prefix.
|
|
86
|
+
*/
|
|
87
|
+
logInfo(message) {
|
|
88
|
+
console.info(`[${this.getApiName()}] ${message}`);
|
|
89
|
+
}
|
|
83
90
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
84
91
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BrowserApiBaseService });
|
|
85
92
|
}
|
|
@@ -87,7 +94,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
87
94
|
type: Injectable
|
|
88
95
|
}] });
|
|
89
96
|
|
|
90
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Extension of `BrowserApiBaseService` for services that need to query
|
|
99
|
+
* browser permissions via the Permissions API.
|
|
100
|
+
*
|
|
101
|
+
* Only services that actively call `requestPermission()` should extend this
|
|
102
|
+
* class. All other services should extend `BrowserApiBaseService` directly
|
|
103
|
+
* to avoid carrying an unnecessary PermissionsService dependency.
|
|
104
|
+
*/
|
|
105
|
+
class PermissionAwareBrowserApiBaseService extends BrowserApiBaseService {
|
|
106
|
+
permissionsService = inject(PermissionsService);
|
|
107
|
+
/**
|
|
108
|
+
* Query the Permissions API for the given permission name.
|
|
109
|
+
* Returns `true` if the permission state is `'granted'`.
|
|
110
|
+
*/
|
|
111
|
+
async requestPermission(permission) {
|
|
112
|
+
if (this.isServerEnvironment()) {
|
|
113
|
+
throw new Error(`${this.getApiName()} API not available in server environment`);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const status = await this.permissionsService.query({ name: permission });
|
|
117
|
+
return status.state === 'granted';
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
this.logError(`Error requesting permission for ${permission}:`, error);
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionAwareBrowserApiBaseService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
125
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionAwareBrowserApiBaseService });
|
|
126
|
+
}
|
|
127
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PermissionAwareBrowserApiBaseService, decorators: [{
|
|
128
|
+
type: Injectable
|
|
129
|
+
}] });
|
|
130
|
+
|
|
131
|
+
class CameraService extends PermissionAwareBrowserApiBaseService {
|
|
91
132
|
currentStream = null;
|
|
92
133
|
getApiName() {
|
|
93
134
|
return 'camera';
|
|
@@ -218,7 +259,7 @@ class GeolocationService extends BrowserApiBaseService {
|
|
|
218
259
|
this.ensureGeolocationSupport();
|
|
219
260
|
return new Promise((resolve, reject) => {
|
|
220
261
|
navigator.geolocation.getCurrentPosition((position) => resolve(position), (error) => {
|
|
221
|
-
|
|
262
|
+
this.logError('Error getting position:', error);
|
|
222
263
|
reject(error);
|
|
223
264
|
}, options);
|
|
224
265
|
});
|
|
@@ -227,7 +268,7 @@ class GeolocationService extends BrowserApiBaseService {
|
|
|
227
268
|
this.ensureGeolocationSupport();
|
|
228
269
|
return new Observable((observer) => {
|
|
229
270
|
const watchId = navigator.geolocation.watchPosition((position) => observer.next(position), (error) => {
|
|
230
|
-
|
|
271
|
+
this.logError('Error watching position:', error);
|
|
231
272
|
observer.error(error);
|
|
232
273
|
}, options);
|
|
233
274
|
return () => {
|
|
@@ -385,7 +426,7 @@ class NotificationService extends BrowserApiBaseService {
|
|
|
385
426
|
return Notification.permission;
|
|
386
427
|
}
|
|
387
428
|
isSupported() {
|
|
388
|
-
return 'Notification' in window;
|
|
429
|
+
return this.isBrowserEnvironment() && 'Notification' in window;
|
|
389
430
|
}
|
|
390
431
|
async requestNotificationPermission() {
|
|
391
432
|
if (!this.isSupported()) {
|
|
@@ -404,7 +445,7 @@ class NotificationService extends BrowserApiBaseService {
|
|
|
404
445
|
return new Notification(title, options);
|
|
405
446
|
}
|
|
406
447
|
catch (error) {
|
|
407
|
-
|
|
448
|
+
this.logError('Error showing notification:', error);
|
|
408
449
|
throw error;
|
|
409
450
|
}
|
|
410
451
|
}
|
|
@@ -415,7 +456,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
415
456
|
type: Injectable
|
|
416
457
|
}] });
|
|
417
458
|
|
|
418
|
-
class ClipboardService extends
|
|
459
|
+
class ClipboardService extends PermissionAwareBrowserApiBaseService {
|
|
419
460
|
getApiName() {
|
|
420
461
|
return 'clipboard';
|
|
421
462
|
}
|
|
@@ -450,16 +491,6 @@ class ClipboardService extends BrowserApiBaseService {
|
|
|
450
491
|
throw error;
|
|
451
492
|
}
|
|
452
493
|
}
|
|
453
|
-
async writeTextSecure(text) {
|
|
454
|
-
await this.ensureClipboardPermission('write');
|
|
455
|
-
try {
|
|
456
|
-
await navigator.clipboard.writeText(text);
|
|
457
|
-
}
|
|
458
|
-
catch (error) {
|
|
459
|
-
console.error('[ClipboardService] Error writing to clipboard:', error);
|
|
460
|
-
throw error;
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
494
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ClipboardService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
464
495
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ClipboardService });
|
|
465
496
|
}
|
|
@@ -493,6 +524,18 @@ const BROWSER_CAPABILITIES = [
|
|
|
493
524
|
{ id: 'serverSentEvents', label: 'Server-Sent Events', requiresSecureContext: false },
|
|
494
525
|
{ id: 'vibration', label: 'Vibration API', requiresSecureContext: false },
|
|
495
526
|
{ id: 'speechSynthesis', label: 'Speech Synthesis API', requiresSecureContext: false },
|
|
527
|
+
{ id: 'mutationObserver', label: 'Mutation Observer', requiresSecureContext: false },
|
|
528
|
+
{ id: 'performanceObserver', label: 'Performance Observer', requiresSecureContext: false },
|
|
529
|
+
{ id: 'idleDetector', label: 'Idle Detection API', requiresSecureContext: true },
|
|
530
|
+
{ id: 'eyeDropper', label: 'EyeDropper API', requiresSecureContext: true },
|
|
531
|
+
{ id: 'barcodeDetector', label: 'Barcode Detection API', requiresSecureContext: true },
|
|
532
|
+
{ id: 'webAudio', label: 'Web Audio API', requiresSecureContext: false },
|
|
533
|
+
{ id: 'gamepad', label: 'Gamepad API', requiresSecureContext: true },
|
|
534
|
+
{ id: 'webBluetooth', label: 'Web Bluetooth API', requiresSecureContext: true },
|
|
535
|
+
{ id: 'webUsb', label: 'WebUSB API', requiresSecureContext: true },
|
|
536
|
+
{ id: 'webNfc', label: 'Web NFC API', requiresSecureContext: true },
|
|
537
|
+
{ id: 'paymentRequest', label: 'Payment Request API', requiresSecureContext: true },
|
|
538
|
+
{ id: 'credentialManagement', label: 'Credential Management API', requiresSecureContext: true },
|
|
496
539
|
];
|
|
497
540
|
class BrowserCapabilityService {
|
|
498
541
|
getCapabilities() {
|
|
@@ -553,6 +596,30 @@ class BrowserCapabilityService {
|
|
|
553
596
|
return typeof navigator !== 'undefined' && 'vibrate' in navigator;
|
|
554
597
|
case 'speechSynthesis':
|
|
555
598
|
return typeof window !== 'undefined' && 'speechSynthesis' in window;
|
|
599
|
+
case 'mutationObserver':
|
|
600
|
+
return typeof MutationObserver !== 'undefined';
|
|
601
|
+
case 'performanceObserver':
|
|
602
|
+
return typeof PerformanceObserver !== 'undefined';
|
|
603
|
+
case 'idleDetector':
|
|
604
|
+
return typeof window !== 'undefined' && 'IdleDetector' in window;
|
|
605
|
+
case 'eyeDropper':
|
|
606
|
+
return typeof window !== 'undefined' && 'EyeDropper' in window;
|
|
607
|
+
case 'barcodeDetector':
|
|
608
|
+
return typeof window !== 'undefined' && 'BarcodeDetector' in window;
|
|
609
|
+
case 'webAudio':
|
|
610
|
+
return typeof window !== 'undefined' && 'AudioContext' in window;
|
|
611
|
+
case 'gamepad':
|
|
612
|
+
return typeof navigator !== 'undefined' && 'getGamepads' in navigator;
|
|
613
|
+
case 'webBluetooth':
|
|
614
|
+
return typeof navigator !== 'undefined' && 'bluetooth' in navigator;
|
|
615
|
+
case 'webUsb':
|
|
616
|
+
return typeof navigator !== 'undefined' && 'usb' in navigator;
|
|
617
|
+
case 'webNfc':
|
|
618
|
+
return typeof window !== 'undefined' && 'NDEFReader' in window;
|
|
619
|
+
case 'paymentRequest':
|
|
620
|
+
return typeof window !== 'undefined' && 'PaymentRequest' in window;
|
|
621
|
+
case 'credentialManagement':
|
|
622
|
+
return typeof navigator !== 'undefined' && 'credentials' in navigator;
|
|
556
623
|
default:
|
|
557
624
|
return false;
|
|
558
625
|
}
|
|
@@ -604,7 +671,6 @@ class BatteryService extends BrowserApiBaseService {
|
|
|
604
671
|
const nav = navigator;
|
|
605
672
|
this.batteryManager = await nav.getBattery();
|
|
606
673
|
const batteryInfo = this.getBatteryInfo();
|
|
607
|
-
this.setupEventListeners();
|
|
608
674
|
return batteryInfo;
|
|
609
675
|
}
|
|
610
676
|
catch (error) {
|
|
@@ -647,16 +713,6 @@ class BatteryService extends BrowserApiBaseService {
|
|
|
647
713
|
};
|
|
648
714
|
});
|
|
649
715
|
}
|
|
650
|
-
setupEventListeners() {
|
|
651
|
-
if (!this.batteryManager)
|
|
652
|
-
return;
|
|
653
|
-
this.batteryManager.addEventListener('chargingchange', () => {
|
|
654
|
-
console.log('[BatteryService] Charging status changed:', this.batteryManager.charging);
|
|
655
|
-
});
|
|
656
|
-
this.batteryManager.addEventListener('levelchange', () => {
|
|
657
|
-
console.log('[BatteryService] Battery level changed:', this.batteryManager.level);
|
|
658
|
-
});
|
|
659
|
-
}
|
|
660
716
|
// Direct access to native battery API
|
|
661
717
|
getNativeBatteryManager() {
|
|
662
718
|
if (!this.batteryManager) {
|
|
@@ -1358,10 +1414,12 @@ function intersectionObserverEntriesStream(element, options = {}) {
|
|
|
1358
1414
|
});
|
|
1359
1415
|
}
|
|
1360
1416
|
|
|
1361
|
-
class IntersectionObserverService {
|
|
1362
|
-
|
|
1417
|
+
class IntersectionObserverService extends BrowserApiBaseService {
|
|
1418
|
+
getApiName() {
|
|
1419
|
+
return 'intersection-observer';
|
|
1420
|
+
}
|
|
1363
1421
|
isSupported() {
|
|
1364
|
-
return
|
|
1422
|
+
return this.isBrowserEnvironment() && 'IntersectionObserver' in window;
|
|
1365
1423
|
}
|
|
1366
1424
|
observe(element, options = {}) {
|
|
1367
1425
|
if (!this.isSupported()) {
|
|
@@ -1375,7 +1433,7 @@ class IntersectionObserverService {
|
|
|
1375
1433
|
}
|
|
1376
1434
|
return intersectionObserverStream(element, options);
|
|
1377
1435
|
}
|
|
1378
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, deps:
|
|
1436
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1379
1437
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService });
|
|
1380
1438
|
}
|
|
1381
1439
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IntersectionObserverService, decorators: [{
|
|
@@ -1416,10 +1474,12 @@ function resizeObserverEntriesStream(element, options = {}) {
|
|
|
1416
1474
|
});
|
|
1417
1475
|
}
|
|
1418
1476
|
|
|
1419
|
-
class ResizeObserverService {
|
|
1420
|
-
|
|
1477
|
+
class ResizeObserverService extends BrowserApiBaseService {
|
|
1478
|
+
getApiName() {
|
|
1479
|
+
return 'resize-observer';
|
|
1480
|
+
}
|
|
1421
1481
|
isSupported() {
|
|
1422
|
-
return
|
|
1482
|
+
return this.isBrowserEnvironment() && 'ResizeObserver' in window;
|
|
1423
1483
|
}
|
|
1424
1484
|
observe(element, options = {}) {
|
|
1425
1485
|
if (!this.isSupported()) {
|
|
@@ -1433,7 +1493,7 @@ class ResizeObserverService {
|
|
|
1433
1493
|
}
|
|
1434
1494
|
return resizeObserverStream(element, options);
|
|
1435
1495
|
}
|
|
1436
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, deps:
|
|
1496
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1437
1497
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService });
|
|
1438
1498
|
}
|
|
1439
1499
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ResizeObserverService, decorators: [{
|
|
@@ -1455,10 +1515,12 @@ function pageVisibilityStream() {
|
|
|
1455
1515
|
});
|
|
1456
1516
|
}
|
|
1457
1517
|
|
|
1458
|
-
class PageVisibilityService {
|
|
1459
|
-
|
|
1518
|
+
class PageVisibilityService extends BrowserApiBaseService {
|
|
1519
|
+
getApiName() {
|
|
1520
|
+
return 'page-visibility';
|
|
1521
|
+
}
|
|
1460
1522
|
isSupported() {
|
|
1461
|
-
return
|
|
1523
|
+
return this.isBrowserEnvironment() && 'hidden' in document;
|
|
1462
1524
|
}
|
|
1463
1525
|
get isHidden() {
|
|
1464
1526
|
if (!this.isSupported())
|
|
@@ -1476,32 +1538,79 @@ class PageVisibilityService {
|
|
|
1476
1538
|
watchVisibility() {
|
|
1477
1539
|
return pageVisibilityStream().pipe(map$1((s) => s === 'visible'));
|
|
1478
1540
|
}
|
|
1479
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, deps:
|
|
1541
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1480
1542
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService });
|
|
1481
1543
|
}
|
|
1482
1544
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PageVisibilityService, decorators: [{
|
|
1483
1545
|
type: Injectable
|
|
1484
1546
|
}] });
|
|
1485
1547
|
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1548
|
+
/**
|
|
1549
|
+
* Base class for services that manage a named registry of persistent
|
|
1550
|
+
* connections (e.g. BroadcastChannel, EventSource, WebSocket pools).
|
|
1551
|
+
*
|
|
1552
|
+
* Implements the Template Method pattern: subclasses define how a single
|
|
1553
|
+
* native connection is closed via `closeNativeConnection()`, while this
|
|
1554
|
+
* class handles the Map lifecycle (remove, closeAll, getActiveKeys).
|
|
1555
|
+
*
|
|
1556
|
+
* Public-facing method names (`close`, `disconnect`, `getOpenChannels`, etc.)
|
|
1557
|
+
* remain the responsibility of the concrete service to preserve the public API.
|
|
1558
|
+
*/
|
|
1559
|
+
class ConnectionRegistryBaseService extends BrowserApiBaseService {
|
|
1560
|
+
connections = new Map();
|
|
1561
|
+
/**
|
|
1562
|
+
* Remove and close the connection registered under `key`.
|
|
1563
|
+
* Safe to call even if the key does not exist.
|
|
1564
|
+
*/
|
|
1565
|
+
removeConnection(key) {
|
|
1566
|
+
const connection = this.connections.get(key);
|
|
1567
|
+
if (connection) {
|
|
1568
|
+
this.closeNativeConnection(connection);
|
|
1569
|
+
this.connections.delete(key);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
/**
|
|
1573
|
+
* Close all registered connections and clear the registry.
|
|
1574
|
+
*/
|
|
1575
|
+
closeAllConnections() {
|
|
1576
|
+
this.connections.forEach((connection) => this.closeNativeConnection(connection));
|
|
1577
|
+
this.connections.clear();
|
|
1578
|
+
}
|
|
1579
|
+
/**
|
|
1580
|
+
* Return the keys of all currently open connections.
|
|
1581
|
+
*/
|
|
1582
|
+
getConnectionKeys() {
|
|
1583
|
+
return Array.from(this.connections.keys());
|
|
1584
|
+
}
|
|
1585
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ConnectionRegistryBaseService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1586
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ConnectionRegistryBaseService });
|
|
1587
|
+
}
|
|
1588
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ConnectionRegistryBaseService, decorators: [{
|
|
1589
|
+
type: Injectable
|
|
1590
|
+
}] });
|
|
1591
|
+
|
|
1592
|
+
class BroadcastChannelService extends ConnectionRegistryBaseService {
|
|
1593
|
+
getApiName() {
|
|
1594
|
+
return 'broadcast-channel';
|
|
1595
|
+
}
|
|
1596
|
+
closeNativeConnection(channel) {
|
|
1597
|
+
channel.close();
|
|
1598
|
+
}
|
|
1490
1599
|
isSupported() {
|
|
1491
|
-
return
|
|
1600
|
+
return this.isBrowserEnvironment() && 'BroadcastChannel' in window;
|
|
1492
1601
|
}
|
|
1493
|
-
|
|
1602
|
+
ensureBroadcastChannelSupported() {
|
|
1494
1603
|
if (!this.isSupported()) {
|
|
1495
1604
|
throw new Error('BroadcastChannel API not supported in this environment');
|
|
1496
1605
|
}
|
|
1497
1606
|
}
|
|
1498
1607
|
open(name) {
|
|
1499
|
-
this.
|
|
1608
|
+
this.ensureBroadcastChannelSupported();
|
|
1500
1609
|
return new Observable((observer) => {
|
|
1501
|
-
let channel = this.
|
|
1610
|
+
let channel = this.connections.get(name);
|
|
1502
1611
|
if (!channel) {
|
|
1503
1612
|
channel = new BroadcastChannel(name);
|
|
1504
|
-
this.
|
|
1613
|
+
this.connections.set(name, channel);
|
|
1505
1614
|
}
|
|
1506
1615
|
const handler = (event) => observer.next(event.data);
|
|
1507
1616
|
const errorHandler = () => observer.error(new Error(`BroadcastChannel "${name}" error`));
|
|
@@ -1516,30 +1625,25 @@ class BroadcastChannelService {
|
|
|
1516
1625
|
});
|
|
1517
1626
|
}
|
|
1518
1627
|
post(name, data) {
|
|
1519
|
-
this.
|
|
1520
|
-
let channel = this.
|
|
1628
|
+
this.ensureBroadcastChannelSupported();
|
|
1629
|
+
let channel = this.connections.get(name);
|
|
1521
1630
|
if (!channel) {
|
|
1522
1631
|
channel = new BroadcastChannel(name);
|
|
1523
|
-
this.
|
|
1632
|
+
this.connections.set(name, channel);
|
|
1524
1633
|
this.destroyRef.onDestroy(() => this.close(name));
|
|
1525
1634
|
}
|
|
1526
1635
|
channel.postMessage(data);
|
|
1527
1636
|
}
|
|
1528
1637
|
close(name) {
|
|
1529
|
-
|
|
1530
|
-
if (channel) {
|
|
1531
|
-
channel.close();
|
|
1532
|
-
this.channels.delete(name);
|
|
1533
|
-
}
|
|
1638
|
+
this.removeConnection(name);
|
|
1534
1639
|
}
|
|
1535
1640
|
closeAll() {
|
|
1536
|
-
this.
|
|
1537
|
-
this.channels.clear();
|
|
1641
|
+
this.closeAllConnections();
|
|
1538
1642
|
}
|
|
1539
1643
|
getOpenChannels() {
|
|
1540
|
-
return
|
|
1644
|
+
return this.getConnectionKeys();
|
|
1541
1645
|
}
|
|
1542
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, deps:
|
|
1646
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1543
1647
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService });
|
|
1544
1648
|
}
|
|
1545
1649
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BroadcastChannelService, decorators: [{
|
|
@@ -1591,21 +1695,23 @@ function networkInformationStream() {
|
|
|
1591
1695
|
});
|
|
1592
1696
|
}
|
|
1593
1697
|
|
|
1594
|
-
class NetworkInformationService {
|
|
1595
|
-
|
|
1698
|
+
class NetworkInformationService extends BrowserApiBaseService {
|
|
1699
|
+
getApiName() {
|
|
1700
|
+
return 'network-information';
|
|
1701
|
+
}
|
|
1596
1702
|
isSupported() {
|
|
1597
|
-
return
|
|
1703
|
+
return this.isBrowserEnvironment() && isNetworkInformationSupported();
|
|
1598
1704
|
}
|
|
1599
1705
|
getSnapshot() {
|
|
1600
|
-
return
|
|
1706
|
+
return this.isBrowserEnvironment() ? getNetworkSnapshot() : { online: true };
|
|
1601
1707
|
}
|
|
1602
1708
|
watch() {
|
|
1603
1709
|
return networkInformationStream();
|
|
1604
1710
|
}
|
|
1605
1711
|
get isOnline() {
|
|
1606
|
-
return
|
|
1712
|
+
return this.isBrowserEnvironment() ? navigator.onLine : true;
|
|
1607
1713
|
}
|
|
1608
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, deps:
|
|
1714
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1609
1715
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService });
|
|
1610
1716
|
}
|
|
1611
1717
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: NetworkInformationService, decorators: [{
|
|
@@ -1708,13 +1814,15 @@ function screenOrientationStream() {
|
|
|
1708
1814
|
});
|
|
1709
1815
|
}
|
|
1710
1816
|
|
|
1711
|
-
class ScreenOrientationService {
|
|
1712
|
-
|
|
1817
|
+
class ScreenOrientationService extends BrowserApiBaseService {
|
|
1818
|
+
getApiName() {
|
|
1819
|
+
return 'screen-orientation';
|
|
1820
|
+
}
|
|
1713
1821
|
isSupported() {
|
|
1714
|
-
return
|
|
1822
|
+
return this.isBrowserEnvironment() && 'screen' in window && 'orientation' in screen;
|
|
1715
1823
|
}
|
|
1716
1824
|
getSnapshot() {
|
|
1717
|
-
return
|
|
1825
|
+
return this.isBrowserEnvironment()
|
|
1718
1826
|
? getOrientationSnapshot()
|
|
1719
1827
|
: { type: 'portrait-primary', angle: 0 };
|
|
1720
1828
|
}
|
|
@@ -1735,7 +1843,7 @@ class ScreenOrientationService {
|
|
|
1735
1843
|
await screen.orientation.lock(orientation);
|
|
1736
1844
|
}
|
|
1737
1845
|
catch (error) {
|
|
1738
|
-
|
|
1846
|
+
this.logError('Failed to lock orientation:', error);
|
|
1739
1847
|
throw error;
|
|
1740
1848
|
}
|
|
1741
1849
|
}
|
|
@@ -1744,30 +1852,31 @@ class ScreenOrientationService {
|
|
|
1744
1852
|
screen.orientation.unlock();
|
|
1745
1853
|
}
|
|
1746
1854
|
}
|
|
1747
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, deps:
|
|
1855
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1748
1856
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService });
|
|
1749
1857
|
}
|
|
1750
1858
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ScreenOrientationService, decorators: [{
|
|
1751
1859
|
type: Injectable
|
|
1752
1860
|
}] });
|
|
1753
1861
|
|
|
1754
|
-
class FullscreenService {
|
|
1755
|
-
|
|
1756
|
-
|
|
1862
|
+
class FullscreenService extends BrowserApiBaseService {
|
|
1863
|
+
getApiName() {
|
|
1864
|
+
return 'fullscreen';
|
|
1865
|
+
}
|
|
1757
1866
|
isSupported() {
|
|
1758
|
-
if (!
|
|
1867
|
+
if (!this.isBrowserEnvironment())
|
|
1759
1868
|
return false;
|
|
1760
1869
|
return !!(document.fullscreenEnabled ??
|
|
1761
1870
|
document.webkitFullscreenEnabled);
|
|
1762
1871
|
}
|
|
1763
1872
|
get isFullscreen() {
|
|
1764
|
-
if (!
|
|
1873
|
+
if (!this.isBrowserEnvironment())
|
|
1765
1874
|
return false;
|
|
1766
1875
|
return !!(document.fullscreenElement ??
|
|
1767
1876
|
document.webkitFullscreenElement);
|
|
1768
1877
|
}
|
|
1769
1878
|
get fullscreenElement() {
|
|
1770
|
-
if (!
|
|
1879
|
+
if (!this.isBrowserEnvironment())
|
|
1771
1880
|
return null;
|
|
1772
1881
|
return (document.fullscreenElement ??
|
|
1773
1882
|
document.webkitFullscreenElement ??
|
|
@@ -1787,7 +1896,7 @@ class FullscreenService {
|
|
|
1787
1896
|
}
|
|
1788
1897
|
}
|
|
1789
1898
|
catch (error) {
|
|
1790
|
-
|
|
1899
|
+
this.logError('Failed to enter fullscreen:', error);
|
|
1791
1900
|
throw error;
|
|
1792
1901
|
}
|
|
1793
1902
|
}
|
|
@@ -1804,7 +1913,7 @@ class FullscreenService {
|
|
|
1804
1913
|
}
|
|
1805
1914
|
}
|
|
1806
1915
|
catch (error) {
|
|
1807
|
-
|
|
1916
|
+
this.logError('Failed to exit fullscreen:', error);
|
|
1808
1917
|
throw error;
|
|
1809
1918
|
}
|
|
1810
1919
|
}
|
|
@@ -1818,7 +1927,7 @@ class FullscreenService {
|
|
|
1818
1927
|
}
|
|
1819
1928
|
watch() {
|
|
1820
1929
|
return new Observable((observer) => {
|
|
1821
|
-
if (!
|
|
1930
|
+
if (!this.isBrowserEnvironment()) {
|
|
1822
1931
|
observer.next(false);
|
|
1823
1932
|
observer.complete();
|
|
1824
1933
|
return undefined;
|
|
@@ -1835,7 +1944,7 @@ class FullscreenService {
|
|
|
1835
1944
|
return cleanup;
|
|
1836
1945
|
});
|
|
1837
1946
|
}
|
|
1838
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, deps:
|
|
1947
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
1839
1948
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService });
|
|
1840
1949
|
}
|
|
1841
1950
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: FullscreenService, decorators: [{
|
|
@@ -2018,23 +2127,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImpor
|
|
|
2018
2127
|
type: Injectable
|
|
2019
2128
|
}] });
|
|
2020
2129
|
|
|
2021
|
-
class ServerSentEventsService {
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2130
|
+
class ServerSentEventsService extends ConnectionRegistryBaseService {
|
|
2131
|
+
getApiName() {
|
|
2132
|
+
return 'server-sent-events';
|
|
2133
|
+
}
|
|
2134
|
+
closeNativeConnection(source) {
|
|
2135
|
+
source.close();
|
|
2136
|
+
}
|
|
2025
2137
|
isSupported() {
|
|
2026
|
-
return
|
|
2138
|
+
return this.isBrowserEnvironment() && 'EventSource' in window;
|
|
2027
2139
|
}
|
|
2028
|
-
|
|
2140
|
+
ensureSSESupported() {
|
|
2029
2141
|
if (!this.isSupported()) {
|
|
2030
2142
|
throw new Error('Server-Sent Events (EventSource) not supported in this environment');
|
|
2031
2143
|
}
|
|
2032
2144
|
}
|
|
2033
2145
|
connect(url, config = {}) {
|
|
2034
|
-
this.
|
|
2146
|
+
this.ensureSSESupported();
|
|
2035
2147
|
return new Observable((observer) => {
|
|
2036
2148
|
const source = new EventSource(url, { withCredentials: config.withCredentials ?? false });
|
|
2037
|
-
this.
|
|
2149
|
+
this.connections.set(url, source);
|
|
2038
2150
|
const messageHandler = (event) => {
|
|
2039
2151
|
try {
|
|
2040
2152
|
observer.next({
|
|
@@ -2058,7 +2170,7 @@ class ServerSentEventsService {
|
|
|
2058
2170
|
observer.error(new Error('SSE connection closed unexpectedly'));
|
|
2059
2171
|
}
|
|
2060
2172
|
else {
|
|
2061
|
-
console.warn(
|
|
2173
|
+
console.warn(`[${this.getApiName()}] SSE connection error, reconnecting...`, event);
|
|
2062
2174
|
}
|
|
2063
2175
|
};
|
|
2064
2176
|
source.addEventListener('message', messageHandler);
|
|
@@ -2076,35 +2188,32 @@ class ServerSentEventsService {
|
|
|
2076
2188
|
});
|
|
2077
2189
|
}
|
|
2078
2190
|
disconnect(url) {
|
|
2079
|
-
|
|
2080
|
-
if (source) {
|
|
2081
|
-
source.close();
|
|
2082
|
-
this.sources.delete(url);
|
|
2083
|
-
}
|
|
2191
|
+
this.removeConnection(url);
|
|
2084
2192
|
}
|
|
2085
2193
|
disconnectAll() {
|
|
2086
|
-
this.
|
|
2087
|
-
this.sources.clear();
|
|
2194
|
+
this.closeAllConnections();
|
|
2088
2195
|
}
|
|
2089
2196
|
getState(url) {
|
|
2090
|
-
const source = this.
|
|
2197
|
+
const source = this.connections.get(url);
|
|
2091
2198
|
if (!source)
|
|
2092
2199
|
return 'closed';
|
|
2093
2200
|
const states = ['connecting', 'open', 'closed'];
|
|
2094
2201
|
return states[source.readyState] ?? 'closed';
|
|
2095
2202
|
}
|
|
2096
2203
|
getActiveConnections() {
|
|
2097
|
-
return
|
|
2204
|
+
return this.getConnectionKeys();
|
|
2098
2205
|
}
|
|
2099
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, deps:
|
|
2206
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2100
2207
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService });
|
|
2101
2208
|
}
|
|
2102
2209
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: ServerSentEventsService, decorators: [{
|
|
2103
2210
|
type: Injectable
|
|
2104
2211
|
}] });
|
|
2105
2212
|
|
|
2106
|
-
class VibrationService {
|
|
2107
|
-
|
|
2213
|
+
class VibrationService extends BrowserApiBaseService {
|
|
2214
|
+
getApiName() {
|
|
2215
|
+
return 'vibration';
|
|
2216
|
+
}
|
|
2108
2217
|
presets = {
|
|
2109
2218
|
success: [50, 30, 50],
|
|
2110
2219
|
error: [100, 50, 100, 50, 100],
|
|
@@ -2112,7 +2221,7 @@ class VibrationService {
|
|
|
2112
2221
|
doubleTap: [50, 100, 50],
|
|
2113
2222
|
};
|
|
2114
2223
|
isSupported() {
|
|
2115
|
-
return
|
|
2224
|
+
return this.isBrowserEnvironment() && 'vibrate' in navigator;
|
|
2116
2225
|
}
|
|
2117
2226
|
vibrate(pattern = 200) {
|
|
2118
2227
|
if (!this.isSupported())
|
|
@@ -2134,20 +2243,21 @@ class VibrationService {
|
|
|
2134
2243
|
stop() {
|
|
2135
2244
|
return this.vibrate(0);
|
|
2136
2245
|
}
|
|
2137
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, deps:
|
|
2246
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2138
2247
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService });
|
|
2139
2248
|
}
|
|
2140
2249
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: VibrationService, decorators: [{
|
|
2141
2250
|
type: Injectable
|
|
2142
2251
|
}] });
|
|
2143
2252
|
|
|
2144
|
-
class SpeechSynthesisService {
|
|
2145
|
-
|
|
2146
|
-
|
|
2253
|
+
class SpeechSynthesisService extends BrowserApiBaseService {
|
|
2254
|
+
getApiName() {
|
|
2255
|
+
return 'speech-synthesis';
|
|
2256
|
+
}
|
|
2147
2257
|
isSupported() {
|
|
2148
|
-
return
|
|
2258
|
+
return this.isBrowserEnvironment() && 'speechSynthesis' in window;
|
|
2149
2259
|
}
|
|
2150
|
-
|
|
2260
|
+
ensureSpeechSynthesisSupported() {
|
|
2151
2261
|
if (!this.isSupported()) {
|
|
2152
2262
|
throw new Error('Speech Synthesis API not supported in this browser');
|
|
2153
2263
|
}
|
|
@@ -2186,7 +2296,7 @@ class SpeechSynthesisService {
|
|
|
2186
2296
|
}
|
|
2187
2297
|
speak(text, options = {}) {
|
|
2188
2298
|
return new Observable((observer) => {
|
|
2189
|
-
this.
|
|
2299
|
+
this.ensureSpeechSynthesisSupported();
|
|
2190
2300
|
const utterance = new SpeechSynthesisUtterance(text);
|
|
2191
2301
|
if (options.lang)
|
|
2192
2302
|
utterance.lang = options.lang;
|
|
@@ -2230,13 +2340,702 @@ class SpeechSynthesisService {
|
|
|
2230
2340
|
if (this.isSupported())
|
|
2231
2341
|
speechSynthesis.cancel();
|
|
2232
2342
|
}
|
|
2233
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, deps:
|
|
2343
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2234
2344
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService });
|
|
2235
2345
|
}
|
|
2236
2346
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: SpeechSynthesisService, decorators: [{
|
|
2237
2347
|
type: Injectable
|
|
2238
2348
|
}] });
|
|
2239
2349
|
|
|
2350
|
+
const DEFAULT_OPTIONS = {
|
|
2351
|
+
childList: true,
|
|
2352
|
+
attributes: true,
|
|
2353
|
+
characterData: false,
|
|
2354
|
+
subtree: true,
|
|
2355
|
+
};
|
|
2356
|
+
function isMutationObserverSupported() {
|
|
2357
|
+
return typeof MutationObserver !== 'undefined';
|
|
2358
|
+
}
|
|
2359
|
+
function mutationObserverStream(element, options = DEFAULT_OPTIONS) {
|
|
2360
|
+
return new Observable((subscriber) => {
|
|
2361
|
+
const observer = new MutationObserver((mutations) => {
|
|
2362
|
+
subscriber.next(mutations);
|
|
2363
|
+
});
|
|
2364
|
+
observer.observe(element, options);
|
|
2365
|
+
return () => observer.disconnect();
|
|
2366
|
+
});
|
|
2367
|
+
}
|
|
2368
|
+
|
|
2369
|
+
class MutationObserverService extends BrowserApiBaseService {
|
|
2370
|
+
getApiName() {
|
|
2371
|
+
return 'mutation-observer';
|
|
2372
|
+
}
|
|
2373
|
+
isSupported() {
|
|
2374
|
+
return this.isBrowserEnvironment() && isMutationObserverSupported();
|
|
2375
|
+
}
|
|
2376
|
+
observe(target, options) {
|
|
2377
|
+
if (!this.isSupported()) {
|
|
2378
|
+
return new Observable((o) => o.error(new Error('MutationObserver API not supported')));
|
|
2379
|
+
}
|
|
2380
|
+
return mutationObserverStream(target, options);
|
|
2381
|
+
}
|
|
2382
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2383
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService });
|
|
2384
|
+
}
|
|
2385
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: MutationObserverService, decorators: [{
|
|
2386
|
+
type: Injectable
|
|
2387
|
+
}] });
|
|
2388
|
+
|
|
2389
|
+
function isPerformanceObserverSupported() {
|
|
2390
|
+
return typeof PerformanceObserver !== 'undefined';
|
|
2391
|
+
}
|
|
2392
|
+
function performanceObserverStream(config) {
|
|
2393
|
+
return new Observable((subscriber) => {
|
|
2394
|
+
const observer = new PerformanceObserver((list) => {
|
|
2395
|
+
subscriber.next(list.getEntries());
|
|
2396
|
+
});
|
|
2397
|
+
if (config.type) {
|
|
2398
|
+
observer.observe({ type: config.type, buffered: config.buffered ?? true });
|
|
2399
|
+
}
|
|
2400
|
+
else if (config.entryTypes) {
|
|
2401
|
+
observer.observe({ entryTypes: config.entryTypes });
|
|
2402
|
+
}
|
|
2403
|
+
return () => observer.disconnect();
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
|
|
2407
|
+
class PerformanceObserverService extends BrowserApiBaseService {
|
|
2408
|
+
getApiName() {
|
|
2409
|
+
return 'performance-observer';
|
|
2410
|
+
}
|
|
2411
|
+
isSupported() {
|
|
2412
|
+
return this.isBrowserEnvironment() && isPerformanceObserverSupported();
|
|
2413
|
+
}
|
|
2414
|
+
observe(config) {
|
|
2415
|
+
if (!this.isSupported()) {
|
|
2416
|
+
return new Observable((o) => o.error(new Error('PerformanceObserver API not supported')));
|
|
2417
|
+
}
|
|
2418
|
+
return performanceObserverStream(config);
|
|
2419
|
+
}
|
|
2420
|
+
observeByType(type, buffered = true) {
|
|
2421
|
+
return this.observe({ type, buffered });
|
|
2422
|
+
}
|
|
2423
|
+
getSupportedEntryTypes() {
|
|
2424
|
+
if (!this.isSupported())
|
|
2425
|
+
return [];
|
|
2426
|
+
return (PerformanceObserver.supportedEntryTypes ?? []);
|
|
2427
|
+
}
|
|
2428
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PerformanceObserverService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2429
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PerformanceObserverService });
|
|
2430
|
+
}
|
|
2431
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PerformanceObserverService, decorators: [{
|
|
2432
|
+
type: Injectable
|
|
2433
|
+
}] });
|
|
2434
|
+
|
|
2435
|
+
function getIdleDetectorClass$1() {
|
|
2436
|
+
return window.IdleDetector;
|
|
2437
|
+
}
|
|
2438
|
+
class IdleDetectorService extends BrowserApiBaseService {
|
|
2439
|
+
getApiName() {
|
|
2440
|
+
return 'idle-detector';
|
|
2441
|
+
}
|
|
2442
|
+
isSupported() {
|
|
2443
|
+
return this.isBrowserEnvironment() && 'IdleDetector' in window;
|
|
2444
|
+
}
|
|
2445
|
+
async requestPermission() {
|
|
2446
|
+
if (!this.isSupported()) {
|
|
2447
|
+
throw new Error('IdleDetector API not supported');
|
|
2448
|
+
}
|
|
2449
|
+
return getIdleDetectorClass$1().requestPermission();
|
|
2450
|
+
}
|
|
2451
|
+
watch(options = {}) {
|
|
2452
|
+
if (!this.isSupported()) {
|
|
2453
|
+
return new Observable((o) => o.error(new Error('IdleDetector API not supported')));
|
|
2454
|
+
}
|
|
2455
|
+
return new Observable((subscriber) => {
|
|
2456
|
+
const abortController = new AbortController();
|
|
2457
|
+
const detector = new (getIdleDetectorClass$1())();
|
|
2458
|
+
detector.addEventListener('change', () => {
|
|
2459
|
+
subscriber.next({
|
|
2460
|
+
user: detector.userState,
|
|
2461
|
+
screen: detector.screenState,
|
|
2462
|
+
});
|
|
2463
|
+
});
|
|
2464
|
+
detector
|
|
2465
|
+
.start({
|
|
2466
|
+
threshold: options.threshold ?? 60_000,
|
|
2467
|
+
signal: abortController.signal,
|
|
2468
|
+
})
|
|
2469
|
+
.catch((err) => subscriber.error(err));
|
|
2470
|
+
return () => abortController.abort();
|
|
2471
|
+
});
|
|
2472
|
+
}
|
|
2473
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IdleDetectorService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2474
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IdleDetectorService });
|
|
2475
|
+
}
|
|
2476
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: IdleDetectorService, decorators: [{
|
|
2477
|
+
type: Injectable
|
|
2478
|
+
}] });
|
|
2479
|
+
|
|
2480
|
+
function getEyeDropperClass() {
|
|
2481
|
+
return window.EyeDropper;
|
|
2482
|
+
}
|
|
2483
|
+
class EyeDropperService extends BrowserApiBaseService {
|
|
2484
|
+
getApiName() {
|
|
2485
|
+
return 'eye-dropper';
|
|
2486
|
+
}
|
|
2487
|
+
isSupported() {
|
|
2488
|
+
return this.isBrowserEnvironment() && 'EyeDropper' in window;
|
|
2489
|
+
}
|
|
2490
|
+
async open(signal) {
|
|
2491
|
+
if (!this.isSupported()) {
|
|
2492
|
+
throw new Error('EyeDropper API not supported');
|
|
2493
|
+
}
|
|
2494
|
+
const dropper = new (getEyeDropperClass())();
|
|
2495
|
+
return dropper.open(signal ? { signal } : undefined);
|
|
2496
|
+
}
|
|
2497
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EyeDropperService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2498
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EyeDropperService });
|
|
2499
|
+
}
|
|
2500
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: EyeDropperService, decorators: [{
|
|
2501
|
+
type: Injectable
|
|
2502
|
+
}] });
|
|
2503
|
+
|
|
2504
|
+
function getBarcodeDetectorClass() {
|
|
2505
|
+
return window.BarcodeDetector;
|
|
2506
|
+
}
|
|
2507
|
+
class BarcodeDetectorService extends BrowserApiBaseService {
|
|
2508
|
+
getApiName() {
|
|
2509
|
+
return 'barcode-detector';
|
|
2510
|
+
}
|
|
2511
|
+
isSupported() {
|
|
2512
|
+
return this.isBrowserEnvironment() && 'BarcodeDetector' in window;
|
|
2513
|
+
}
|
|
2514
|
+
async getSupportedFormats() {
|
|
2515
|
+
if (!this.isSupported())
|
|
2516
|
+
return [];
|
|
2517
|
+
return getBarcodeDetectorClass().getSupportedFormats();
|
|
2518
|
+
}
|
|
2519
|
+
async detect(image, formats) {
|
|
2520
|
+
if (!this.isSupported()) {
|
|
2521
|
+
throw new Error('BarcodeDetector API not supported');
|
|
2522
|
+
}
|
|
2523
|
+
const detector = formats
|
|
2524
|
+
? new (getBarcodeDetectorClass())({ formats })
|
|
2525
|
+
: new (getBarcodeDetectorClass())();
|
|
2526
|
+
return detector.detect(image);
|
|
2527
|
+
}
|
|
2528
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BarcodeDetectorService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2529
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BarcodeDetectorService });
|
|
2530
|
+
}
|
|
2531
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: BarcodeDetectorService, decorators: [{
|
|
2532
|
+
type: Injectable
|
|
2533
|
+
}] });
|
|
2534
|
+
|
|
2535
|
+
class WebAudioService extends BrowserApiBaseService {
|
|
2536
|
+
getApiName() {
|
|
2537
|
+
return 'web-audio';
|
|
2538
|
+
}
|
|
2539
|
+
context = null;
|
|
2540
|
+
isSupported() {
|
|
2541
|
+
return this.isBrowserEnvironment() && 'AudioContext' in window;
|
|
2542
|
+
}
|
|
2543
|
+
getContext() {
|
|
2544
|
+
if (!this.isSupported()) {
|
|
2545
|
+
throw new Error('Web Audio API not supported');
|
|
2546
|
+
}
|
|
2547
|
+
if (!this.context || this.context.state === 'closed') {
|
|
2548
|
+
this.context = new AudioContext();
|
|
2549
|
+
this.destroyRef.onDestroy(() => this.close());
|
|
2550
|
+
}
|
|
2551
|
+
return this.context;
|
|
2552
|
+
}
|
|
2553
|
+
async resume() {
|
|
2554
|
+
const ctx = this.getContext();
|
|
2555
|
+
if (ctx.state === 'suspended') {
|
|
2556
|
+
await ctx.resume();
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
async close() {
|
|
2560
|
+
if (this.context && this.context.state !== 'closed') {
|
|
2561
|
+
await this.context.close();
|
|
2562
|
+
this.context = null;
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
getState() {
|
|
2566
|
+
return (this.context?.state ?? 'closed');
|
|
2567
|
+
}
|
|
2568
|
+
createOscillator(type = 'sine', frequency = 440) {
|
|
2569
|
+
const ctx = this.getContext();
|
|
2570
|
+
const oscillator = ctx.createOscillator();
|
|
2571
|
+
oscillator.type = type;
|
|
2572
|
+
oscillator.frequency.value = frequency;
|
|
2573
|
+
return oscillator;
|
|
2574
|
+
}
|
|
2575
|
+
createGain(value = 1) {
|
|
2576
|
+
const ctx = this.getContext();
|
|
2577
|
+
const gain = ctx.createGain();
|
|
2578
|
+
gain.gain.value = value;
|
|
2579
|
+
return gain;
|
|
2580
|
+
}
|
|
2581
|
+
createAnalyser(fftSize = 2048) {
|
|
2582
|
+
const ctx = this.getContext();
|
|
2583
|
+
const analyser = ctx.createAnalyser();
|
|
2584
|
+
analyser.fftSize = fftSize;
|
|
2585
|
+
return analyser;
|
|
2586
|
+
}
|
|
2587
|
+
watchAnalyser(analyser, intervalMs = 50) {
|
|
2588
|
+
return new Observable((subscriber) => {
|
|
2589
|
+
const frequencyData = new Uint8Array(analyser.frequencyBinCount);
|
|
2590
|
+
const timeDomainData = new Uint8Array(analyser.frequencyBinCount);
|
|
2591
|
+
const interval = setInterval(() => {
|
|
2592
|
+
analyser.getByteFrequencyData(frequencyData);
|
|
2593
|
+
analyser.getByteTimeDomainData(timeDomainData);
|
|
2594
|
+
subscriber.next({
|
|
2595
|
+
frequencyData: new Uint8Array(frequencyData),
|
|
2596
|
+
timeDomainData: new Uint8Array(timeDomainData),
|
|
2597
|
+
});
|
|
2598
|
+
}, intervalMs);
|
|
2599
|
+
return () => clearInterval(interval);
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
async decodeAudioData(arrayBuffer) {
|
|
2603
|
+
const ctx = this.getContext();
|
|
2604
|
+
return ctx.decodeAudioData(arrayBuffer);
|
|
2605
|
+
}
|
|
2606
|
+
playBuffer(buffer, loop = false) {
|
|
2607
|
+
const ctx = this.getContext();
|
|
2608
|
+
const source = ctx.createBufferSource();
|
|
2609
|
+
source.buffer = buffer;
|
|
2610
|
+
source.loop = loop;
|
|
2611
|
+
source.connect(ctx.destination);
|
|
2612
|
+
source.start(0);
|
|
2613
|
+
return source;
|
|
2614
|
+
}
|
|
2615
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebAudioService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2616
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebAudioService });
|
|
2617
|
+
}
|
|
2618
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebAudioService, decorators: [{
|
|
2619
|
+
type: Injectable
|
|
2620
|
+
}] });
|
|
2621
|
+
|
|
2622
|
+
function isGamepadSupported() {
|
|
2623
|
+
return typeof navigator !== 'undefined' && 'getGamepads' in navigator;
|
|
2624
|
+
}
|
|
2625
|
+
function gamepadSnapshot(index) {
|
|
2626
|
+
const gp = navigator.getGamepads()[index];
|
|
2627
|
+
if (!gp)
|
|
2628
|
+
return null;
|
|
2629
|
+
return {
|
|
2630
|
+
id: gp.id,
|
|
2631
|
+
index: gp.index,
|
|
2632
|
+
connected: gp.connected,
|
|
2633
|
+
buttons: gp.buttons.map((b) => ({ pressed: b.pressed, value: b.value })),
|
|
2634
|
+
axes: Array.from(gp.axes),
|
|
2635
|
+
timestamp: gp.timestamp,
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
function gamepadConnectionStream() {
|
|
2639
|
+
return new Observable((subscriber) => {
|
|
2640
|
+
const onConnect = (e) => {
|
|
2641
|
+
const gp = e.gamepad;
|
|
2642
|
+
subscriber.next({
|
|
2643
|
+
type: 'connected',
|
|
2644
|
+
gamepad: {
|
|
2645
|
+
id: gp.id,
|
|
2646
|
+
index: gp.index,
|
|
2647
|
+
connected: true,
|
|
2648
|
+
buttons: gp.buttons.map((b) => ({ pressed: b.pressed, value: b.value })),
|
|
2649
|
+
axes: Array.from(gp.axes),
|
|
2650
|
+
timestamp: gp.timestamp,
|
|
2651
|
+
},
|
|
2652
|
+
});
|
|
2653
|
+
};
|
|
2654
|
+
const onDisconnect = (e) => {
|
|
2655
|
+
subscriber.next({
|
|
2656
|
+
type: 'disconnected',
|
|
2657
|
+
gamepad: {
|
|
2658
|
+
id: e.gamepad.id,
|
|
2659
|
+
index: e.gamepad.index,
|
|
2660
|
+
connected: false,
|
|
2661
|
+
buttons: [],
|
|
2662
|
+
axes: [],
|
|
2663
|
+
timestamp: e.gamepad.timestamp,
|
|
2664
|
+
},
|
|
2665
|
+
});
|
|
2666
|
+
};
|
|
2667
|
+
window.addEventListener('gamepadconnected', onConnect);
|
|
2668
|
+
window.addEventListener('gamepaddisconnected', onDisconnect);
|
|
2669
|
+
return () => {
|
|
2670
|
+
window.removeEventListener('gamepadconnected', onConnect);
|
|
2671
|
+
window.removeEventListener('gamepaddisconnected', onDisconnect);
|
|
2672
|
+
};
|
|
2673
|
+
});
|
|
2674
|
+
}
|
|
2675
|
+
function gamepadPollStream(index, intervalMs = 16) {
|
|
2676
|
+
return new Observable((subscriber) => {
|
|
2677
|
+
let rafId;
|
|
2678
|
+
const poll = () => {
|
|
2679
|
+
const state = gamepadSnapshot(index);
|
|
2680
|
+
if (state) {
|
|
2681
|
+
subscriber.next(state);
|
|
2682
|
+
}
|
|
2683
|
+
rafId = requestAnimationFrame(poll);
|
|
2684
|
+
};
|
|
2685
|
+
if (intervalMs <= 16) {
|
|
2686
|
+
rafId = requestAnimationFrame(poll);
|
|
2687
|
+
return () => cancelAnimationFrame(rafId);
|
|
2688
|
+
}
|
|
2689
|
+
const intervalId = setInterval(() => {
|
|
2690
|
+
const state = gamepadSnapshot(index);
|
|
2691
|
+
if (state)
|
|
2692
|
+
subscriber.next(state);
|
|
2693
|
+
}, intervalMs);
|
|
2694
|
+
return () => clearInterval(intervalId);
|
|
2695
|
+
});
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
class GamepadService extends BrowserApiBaseService {
|
|
2699
|
+
getApiName() {
|
|
2700
|
+
return 'gamepad';
|
|
2701
|
+
}
|
|
2702
|
+
isSupported() {
|
|
2703
|
+
return this.isBrowserEnvironment() && isGamepadSupported();
|
|
2704
|
+
}
|
|
2705
|
+
getSnapshot(index) {
|
|
2706
|
+
if (!this.isSupported())
|
|
2707
|
+
return null;
|
|
2708
|
+
return gamepadSnapshot(index);
|
|
2709
|
+
}
|
|
2710
|
+
getConnectedGamepads() {
|
|
2711
|
+
if (!this.isSupported())
|
|
2712
|
+
return [];
|
|
2713
|
+
return navigator
|
|
2714
|
+
.getGamepads()
|
|
2715
|
+
.filter((gp) => gp !== null)
|
|
2716
|
+
.map((gp) => ({
|
|
2717
|
+
id: gp.id,
|
|
2718
|
+
index: gp.index,
|
|
2719
|
+
connected: gp.connected,
|
|
2720
|
+
buttons: gp.buttons.map((b) => ({ pressed: b.pressed, value: b.value })),
|
|
2721
|
+
axes: Array.from(gp.axes),
|
|
2722
|
+
timestamp: gp.timestamp,
|
|
2723
|
+
}));
|
|
2724
|
+
}
|
|
2725
|
+
watchConnections() {
|
|
2726
|
+
if (!this.isSupported()) {
|
|
2727
|
+
return new Observable((o) => o.error(new Error('Gamepad API not supported')));
|
|
2728
|
+
}
|
|
2729
|
+
return gamepadConnectionStream();
|
|
2730
|
+
}
|
|
2731
|
+
poll(index, intervalMs = 16) {
|
|
2732
|
+
if (!this.isSupported()) {
|
|
2733
|
+
return new Observable((o) => o.error(new Error('Gamepad API not supported')));
|
|
2734
|
+
}
|
|
2735
|
+
return gamepadPollStream(index, intervalMs);
|
|
2736
|
+
}
|
|
2737
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GamepadService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2738
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GamepadService });
|
|
2739
|
+
}
|
|
2740
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: GamepadService, decorators: [{
|
|
2741
|
+
type: Injectable
|
|
2742
|
+
}] });
|
|
2743
|
+
|
|
2744
|
+
function getBluetooth() {
|
|
2745
|
+
return navigator.bluetooth;
|
|
2746
|
+
}
|
|
2747
|
+
class WebBluetoothService extends BrowserApiBaseService {
|
|
2748
|
+
getApiName() {
|
|
2749
|
+
return 'web-bluetooth';
|
|
2750
|
+
}
|
|
2751
|
+
isSupported() {
|
|
2752
|
+
return this.isBrowserEnvironment() && !!getBluetooth();
|
|
2753
|
+
}
|
|
2754
|
+
async requestDevice(options = { acceptAllDevices: true }) {
|
|
2755
|
+
if (!this.isSupported()) {
|
|
2756
|
+
throw new Error('Web Bluetooth API not supported');
|
|
2757
|
+
}
|
|
2758
|
+
return getBluetooth().requestDevice(options);
|
|
2759
|
+
}
|
|
2760
|
+
async connect(device) {
|
|
2761
|
+
if (!device.gatt) {
|
|
2762
|
+
throw new Error('GATT server not available on this device');
|
|
2763
|
+
}
|
|
2764
|
+
return device.gatt.connect();
|
|
2765
|
+
}
|
|
2766
|
+
disconnect(device) {
|
|
2767
|
+
device.gatt?.disconnect();
|
|
2768
|
+
}
|
|
2769
|
+
watchDisconnection(device) {
|
|
2770
|
+
return new Observable((subscriber) => {
|
|
2771
|
+
const handler = () => subscriber.next();
|
|
2772
|
+
device.addEventListener('gattserverdisconnected', handler);
|
|
2773
|
+
return () => device.removeEventListener('gattserverdisconnected', handler);
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
async readCharacteristic(server, serviceUuid, characteristicUuid) {
|
|
2777
|
+
const service = await server.getPrimaryService(serviceUuid);
|
|
2778
|
+
const characteristic = await service.getCharacteristic(characteristicUuid);
|
|
2779
|
+
return characteristic.readValue();
|
|
2780
|
+
}
|
|
2781
|
+
async writeCharacteristic(server, serviceUuid, characteristicUuid, value) {
|
|
2782
|
+
const service = await server.getPrimaryService(serviceUuid);
|
|
2783
|
+
const characteristic = await service.getCharacteristic(characteristicUuid);
|
|
2784
|
+
await characteristic.writeValue(value);
|
|
2785
|
+
}
|
|
2786
|
+
watchCharacteristic(server, serviceUuid, characteristicUuid) {
|
|
2787
|
+
return new Observable((subscriber) => {
|
|
2788
|
+
let characteristic;
|
|
2789
|
+
server
|
|
2790
|
+
.getPrimaryService(serviceUuid)
|
|
2791
|
+
.then((service) => service.getCharacteristic(characteristicUuid))
|
|
2792
|
+
.then((char) => {
|
|
2793
|
+
characteristic = char;
|
|
2794
|
+
const handler = (event) => {
|
|
2795
|
+
const target = event.target;
|
|
2796
|
+
if (target.value)
|
|
2797
|
+
subscriber.next(target.value);
|
|
2798
|
+
};
|
|
2799
|
+
characteristic.addEventListener('characteristicvaluechanged', handler);
|
|
2800
|
+
return characteristic.startNotifications();
|
|
2801
|
+
})
|
|
2802
|
+
.catch((err) => subscriber.error(err));
|
|
2803
|
+
return () => {
|
|
2804
|
+
characteristic?.stopNotifications().catch(() => { });
|
|
2805
|
+
};
|
|
2806
|
+
});
|
|
2807
|
+
}
|
|
2808
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebBluetoothService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2809
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebBluetoothService });
|
|
2810
|
+
}
|
|
2811
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebBluetoothService, decorators: [{
|
|
2812
|
+
type: Injectable
|
|
2813
|
+
}] });
|
|
2814
|
+
|
|
2815
|
+
function getUsb() {
|
|
2816
|
+
return navigator.usb;
|
|
2817
|
+
}
|
|
2818
|
+
class WebUsbService extends BrowserApiBaseService {
|
|
2819
|
+
getApiName() {
|
|
2820
|
+
return 'web-usb';
|
|
2821
|
+
}
|
|
2822
|
+
isSupported() {
|
|
2823
|
+
return this.isBrowserEnvironment() && !!getUsb();
|
|
2824
|
+
}
|
|
2825
|
+
async requestDevice(filters = []) {
|
|
2826
|
+
if (!this.isSupported()) {
|
|
2827
|
+
throw new Error('WebUSB API not supported');
|
|
2828
|
+
}
|
|
2829
|
+
return getUsb().requestDevice({ filters });
|
|
2830
|
+
}
|
|
2831
|
+
async getDevices() {
|
|
2832
|
+
if (!this.isSupported())
|
|
2833
|
+
return [];
|
|
2834
|
+
return getUsb().getDevices();
|
|
2835
|
+
}
|
|
2836
|
+
async open(device) {
|
|
2837
|
+
await device.open();
|
|
2838
|
+
}
|
|
2839
|
+
async close(device) {
|
|
2840
|
+
await device.close();
|
|
2841
|
+
}
|
|
2842
|
+
async selectConfiguration(device, configurationValue) {
|
|
2843
|
+
await device.selectConfiguration(configurationValue);
|
|
2844
|
+
}
|
|
2845
|
+
async claimInterface(device, interfaceNumber) {
|
|
2846
|
+
await device.claimInterface(interfaceNumber);
|
|
2847
|
+
}
|
|
2848
|
+
async releaseInterface(device, interfaceNumber) {
|
|
2849
|
+
await device.releaseInterface(interfaceNumber);
|
|
2850
|
+
}
|
|
2851
|
+
async transferIn(device, endpointNumber, length) {
|
|
2852
|
+
return device.transferIn(endpointNumber, length);
|
|
2853
|
+
}
|
|
2854
|
+
async transferOut(device, endpointNumber, data) {
|
|
2855
|
+
return device.transferOut(endpointNumber, data);
|
|
2856
|
+
}
|
|
2857
|
+
watchConnection() {
|
|
2858
|
+
if (!this.isSupported()) {
|
|
2859
|
+
return new Observable((o) => o.error(new Error('WebUSB API not supported')));
|
|
2860
|
+
}
|
|
2861
|
+
return new Observable((subscriber) => {
|
|
2862
|
+
const usb = getUsb();
|
|
2863
|
+
const onConnect = (e) => subscriber.next({ device: e.device, type: 'connect' });
|
|
2864
|
+
const onDisconnect = (e) => subscriber.next({ device: e.device, type: 'disconnect' });
|
|
2865
|
+
usb.addEventListener('connect', onConnect);
|
|
2866
|
+
usb.addEventListener('disconnect', onDisconnect);
|
|
2867
|
+
return () => {
|
|
2868
|
+
usb.removeEventListener('connect', onConnect);
|
|
2869
|
+
usb.removeEventListener('disconnect', onDisconnect);
|
|
2870
|
+
};
|
|
2871
|
+
});
|
|
2872
|
+
}
|
|
2873
|
+
getDeviceInfo(device) {
|
|
2874
|
+
return {
|
|
2875
|
+
vendorId: device.vendorId,
|
|
2876
|
+
productId: device.productId,
|
|
2877
|
+
productName: device.productName,
|
|
2878
|
+
manufacturerName: device.manufacturerName,
|
|
2879
|
+
serialNumber: device.serialNumber,
|
|
2880
|
+
opened: device.opened,
|
|
2881
|
+
};
|
|
2882
|
+
}
|
|
2883
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebUsbService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2884
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebUsbService });
|
|
2885
|
+
}
|
|
2886
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebUsbService, decorators: [{
|
|
2887
|
+
type: Injectable
|
|
2888
|
+
}] });
|
|
2889
|
+
|
|
2890
|
+
function getNdefReaderClass() {
|
|
2891
|
+
return window.NDEFReader;
|
|
2892
|
+
}
|
|
2893
|
+
class WebNfcService extends BrowserApiBaseService {
|
|
2894
|
+
getApiName() {
|
|
2895
|
+
return 'web-nfc';
|
|
2896
|
+
}
|
|
2897
|
+
isSupported() {
|
|
2898
|
+
return this.isBrowserEnvironment() && 'NDEFReader' in window;
|
|
2899
|
+
}
|
|
2900
|
+
scan() {
|
|
2901
|
+
if (!this.isSupported()) {
|
|
2902
|
+
return new Observable((o) => o.error(new Error('Web NFC API not supported')));
|
|
2903
|
+
}
|
|
2904
|
+
return new Observable((subscriber) => {
|
|
2905
|
+
const abortController = new AbortController();
|
|
2906
|
+
const reader = new (getNdefReaderClass())();
|
|
2907
|
+
const onReading = (event) => {
|
|
2908
|
+
const e = event;
|
|
2909
|
+
subscriber.next({
|
|
2910
|
+
serialNumber: e.serialNumber,
|
|
2911
|
+
message: e.message,
|
|
2912
|
+
});
|
|
2913
|
+
};
|
|
2914
|
+
const onError = (event) => {
|
|
2915
|
+
subscriber.error(event.error ?? new Error('NFC read error'));
|
|
2916
|
+
};
|
|
2917
|
+
reader.addEventListener('reading', onReading);
|
|
2918
|
+
reader.addEventListener('readingerror', onError);
|
|
2919
|
+
reader
|
|
2920
|
+
.scan({ signal: abortController.signal })
|
|
2921
|
+
.catch((err) => subscriber.error(err));
|
|
2922
|
+
return () => abortController.abort();
|
|
2923
|
+
});
|
|
2924
|
+
}
|
|
2925
|
+
async write(message, options) {
|
|
2926
|
+
if (!this.isSupported()) {
|
|
2927
|
+
throw new Error('Web NFC API not supported');
|
|
2928
|
+
}
|
|
2929
|
+
const reader = new (getNdefReaderClass())();
|
|
2930
|
+
await reader.write(message, options);
|
|
2931
|
+
}
|
|
2932
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebNfcService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2933
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebNfcService });
|
|
2934
|
+
}
|
|
2935
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: WebNfcService, decorators: [{
|
|
2936
|
+
type: Injectable
|
|
2937
|
+
}] });
|
|
2938
|
+
|
|
2939
|
+
class PaymentRequestService extends BrowserApiBaseService {
|
|
2940
|
+
getApiName() {
|
|
2941
|
+
return 'payment-request';
|
|
2942
|
+
}
|
|
2943
|
+
isSupported() {
|
|
2944
|
+
return this.isBrowserEnvironment() && 'PaymentRequest' in window;
|
|
2945
|
+
}
|
|
2946
|
+
async canMakePayment(methods, details) {
|
|
2947
|
+
if (!this.isSupported())
|
|
2948
|
+
return false;
|
|
2949
|
+
const request = new PaymentRequest(methods, details);
|
|
2950
|
+
return request.canMakePayment();
|
|
2951
|
+
}
|
|
2952
|
+
async show(methods, details, options) {
|
|
2953
|
+
if (!this.isSupported()) {
|
|
2954
|
+
throw new Error('Payment Request API not supported');
|
|
2955
|
+
}
|
|
2956
|
+
const request = new PaymentRequest(methods, details, options);
|
|
2957
|
+
const response = await request.show();
|
|
2958
|
+
const result = {
|
|
2959
|
+
methodName: response.methodName,
|
|
2960
|
+
details: response.details,
|
|
2961
|
+
payerName: response.payerName ?? null,
|
|
2962
|
+
payerEmail: response.payerEmail ?? null,
|
|
2963
|
+
payerPhone: response.payerPhone ?? null,
|
|
2964
|
+
};
|
|
2965
|
+
await response.complete('success');
|
|
2966
|
+
return result;
|
|
2967
|
+
}
|
|
2968
|
+
async abort(methods, details) {
|
|
2969
|
+
if (!this.isSupported())
|
|
2970
|
+
return;
|
|
2971
|
+
const request = new PaymentRequest(methods, details);
|
|
2972
|
+
await request.abort();
|
|
2973
|
+
}
|
|
2974
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PaymentRequestService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
2975
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PaymentRequestService });
|
|
2976
|
+
}
|
|
2977
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: PaymentRequestService, decorators: [{
|
|
2978
|
+
type: Injectable
|
|
2979
|
+
}] });
|
|
2980
|
+
|
|
2981
|
+
class CredentialManagementService extends BrowserApiBaseService {
|
|
2982
|
+
getApiName() {
|
|
2983
|
+
return 'credential-management';
|
|
2984
|
+
}
|
|
2985
|
+
isSupported() {
|
|
2986
|
+
return this.isBrowserEnvironment() && 'credentials' in navigator;
|
|
2987
|
+
}
|
|
2988
|
+
isPublicKeySupported() {
|
|
2989
|
+
return this.isSupported() && 'PublicKeyCredential' in window;
|
|
2990
|
+
}
|
|
2991
|
+
async get(options) {
|
|
2992
|
+
if (!this.isSupported()) {
|
|
2993
|
+
throw new Error('Credential Management API not supported');
|
|
2994
|
+
}
|
|
2995
|
+
return navigator.credentials.get(options);
|
|
2996
|
+
}
|
|
2997
|
+
async store(credential) {
|
|
2998
|
+
if (!this.isSupported()) {
|
|
2999
|
+
throw new Error('Credential Management API not supported');
|
|
3000
|
+
}
|
|
3001
|
+
await navigator.credentials.store(credential);
|
|
3002
|
+
}
|
|
3003
|
+
async createPasswordCredential(data) {
|
|
3004
|
+
if (!this.isSupported()) {
|
|
3005
|
+
throw new Error('Credential Management API not supported');
|
|
3006
|
+
}
|
|
3007
|
+
return navigator.credentials.create({
|
|
3008
|
+
password: data,
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
3011
|
+
async createPublicKeyCredential(options) {
|
|
3012
|
+
if (!this.isPublicKeySupported()) {
|
|
3013
|
+
throw new Error('PublicKeyCredential API not supported');
|
|
3014
|
+
}
|
|
3015
|
+
return navigator.credentials.create({
|
|
3016
|
+
publicKey: options,
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3019
|
+
async preventSilentAccess() {
|
|
3020
|
+
if (!this.isSupported())
|
|
3021
|
+
return;
|
|
3022
|
+
await navigator.credentials.preventSilentAccess();
|
|
3023
|
+
}
|
|
3024
|
+
async isConditionalMediationAvailable() {
|
|
3025
|
+
if (!this.isPublicKeySupported())
|
|
3026
|
+
return false;
|
|
3027
|
+
if ('isConditionalMediationAvailable' in PublicKeyCredential) {
|
|
3028
|
+
return PublicKeyCredential.isConditionalMediationAvailable();
|
|
3029
|
+
}
|
|
3030
|
+
return false;
|
|
3031
|
+
}
|
|
3032
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CredentialManagementService, deps: null, target: i0.ɵɵFactoryTarget.Injectable });
|
|
3033
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CredentialManagementService });
|
|
3034
|
+
}
|
|
3035
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.4", ngImport: i0, type: CredentialManagementService, decorators: [{
|
|
3036
|
+
type: Injectable
|
|
3037
|
+
}] });
|
|
3038
|
+
|
|
2240
3039
|
// Common types for browser APIs
|
|
2241
3040
|
|
|
2242
3041
|
function injectPageVisibility() {
|
|
@@ -2260,9 +3059,23 @@ function injectResizeObserver(elementOrRef, options) {
|
|
|
2260
3059
|
const platformId = inject(PLATFORM_ID);
|
|
2261
3060
|
const size = signal(null, ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
2262
3061
|
if (isPlatformBrowser(platformId) && isResizeObserverSupported()) {
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
3062
|
+
if (isSignal(elementOrRef)) {
|
|
3063
|
+
let sub;
|
|
3064
|
+
effect((onCleanup) => {
|
|
3065
|
+
sub?.unsubscribe();
|
|
3066
|
+
const raw = elementOrRef();
|
|
3067
|
+
if (raw) {
|
|
3068
|
+
const el = raw instanceof ElementRef ? raw.nativeElement : raw;
|
|
3069
|
+
sub = resizeObserverStream(el, options).subscribe((s) => size.set(s));
|
|
3070
|
+
}
|
|
3071
|
+
onCleanup(() => sub?.unsubscribe());
|
|
3072
|
+
});
|
|
3073
|
+
}
|
|
3074
|
+
else {
|
|
3075
|
+
const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
|
|
3076
|
+
const sub = resizeObserverStream(el, options).subscribe((s) => size.set(s));
|
|
3077
|
+
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
3078
|
+
}
|
|
2266
3079
|
}
|
|
2267
3080
|
return {
|
|
2268
3081
|
size: size.asReadonly(),
|
|
@@ -2278,9 +3091,23 @@ function injectIntersectionObserver(elementOrRef, options) {
|
|
|
2278
3091
|
const platformId = inject(PLATFORM_ID);
|
|
2279
3092
|
const isIntersecting = signal(false, ...(ngDevMode ? [{ debugName: "isIntersecting" }] : /* istanbul ignore next */ []));
|
|
2280
3093
|
if (isPlatformBrowser(platformId) && isIntersectionObserverSupported()) {
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
3094
|
+
if (isSignal(elementOrRef)) {
|
|
3095
|
+
let sub;
|
|
3096
|
+
effect((onCleanup) => {
|
|
3097
|
+
sub?.unsubscribe();
|
|
3098
|
+
const raw = elementOrRef();
|
|
3099
|
+
if (raw) {
|
|
3100
|
+
const el = raw instanceof ElementRef ? raw.nativeElement : raw;
|
|
3101
|
+
sub = intersectionObserverStream(el, options).subscribe((v) => isIntersecting.set(v));
|
|
3102
|
+
}
|
|
3103
|
+
onCleanup(() => sub?.unsubscribe());
|
|
3104
|
+
});
|
|
3105
|
+
}
|
|
3106
|
+
else {
|
|
3107
|
+
const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
|
|
3108
|
+
const sub = intersectionObserverStream(el, options).subscribe((v) => isIntersecting.set(v));
|
|
3109
|
+
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
3110
|
+
}
|
|
2284
3111
|
}
|
|
2285
3112
|
return {
|
|
2286
3113
|
isIntersecting: isIntersecting.asReadonly(),
|
|
@@ -2328,6 +3155,102 @@ function injectScreenOrientation() {
|
|
|
2328
3155
|
};
|
|
2329
3156
|
}
|
|
2330
3157
|
|
|
3158
|
+
function injectMutationObserver(elementOrRef, options) {
|
|
3159
|
+
const destroyRef = inject(DestroyRef);
|
|
3160
|
+
const platformId = inject(PLATFORM_ID);
|
|
3161
|
+
const mutations = signal([], ...(ngDevMode ? [{ debugName: "mutations" }] : /* istanbul ignore next */ []));
|
|
3162
|
+
if (isPlatformBrowser(platformId) && isMutationObserverSupported()) {
|
|
3163
|
+
if (isSignal(elementOrRef)) {
|
|
3164
|
+
let sub;
|
|
3165
|
+
effect((onCleanup) => {
|
|
3166
|
+
sub?.unsubscribe();
|
|
3167
|
+
const raw = elementOrRef();
|
|
3168
|
+
if (raw) {
|
|
3169
|
+
const el = raw instanceof ElementRef ? raw.nativeElement : raw;
|
|
3170
|
+
sub = mutationObserverStream(el, options).subscribe((m) => mutations.set(m));
|
|
3171
|
+
}
|
|
3172
|
+
onCleanup(() => sub?.unsubscribe());
|
|
3173
|
+
});
|
|
3174
|
+
}
|
|
3175
|
+
else {
|
|
3176
|
+
const el = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef;
|
|
3177
|
+
const sub = mutationObserverStream(el, options).subscribe((m) => mutations.set(m));
|
|
3178
|
+
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
3179
|
+
}
|
|
3180
|
+
}
|
|
3181
|
+
return {
|
|
3182
|
+
mutations: mutations.asReadonly(),
|
|
3183
|
+
mutationCount: computed(() => mutations().length),
|
|
3184
|
+
};
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
function injectPerformanceObserver(config) {
|
|
3188
|
+
const destroyRef = inject(DestroyRef);
|
|
3189
|
+
const platformId = inject(PLATFORM_ID);
|
|
3190
|
+
const entries = signal([], ...(ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []));
|
|
3191
|
+
if (isPlatformBrowser(platformId) && isPerformanceObserverSupported()) {
|
|
3192
|
+
const sub = performanceObserverStream(config).subscribe((e) => entries.set(e));
|
|
3193
|
+
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
3194
|
+
}
|
|
3195
|
+
return {
|
|
3196
|
+
entries: entries.asReadonly(),
|
|
3197
|
+
entryCount: computed(() => entries().length),
|
|
3198
|
+
latestEntry: computed(() => entries().at(-1)),
|
|
3199
|
+
};
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
function getIdleDetectorClass() {
|
|
3203
|
+
return window.IdleDetector;
|
|
3204
|
+
}
|
|
3205
|
+
function injectIdleDetector(options = {}) {
|
|
3206
|
+
const destroyRef = inject(DestroyRef);
|
|
3207
|
+
const platformId = inject(PLATFORM_ID);
|
|
3208
|
+
const defaultState = { user: 'active', screen: 'unlocked' };
|
|
3209
|
+
const state = signal(defaultState, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
3210
|
+
if (isPlatformBrowser(platformId) && 'IdleDetector' in window) {
|
|
3211
|
+
const abortController = new AbortController();
|
|
3212
|
+
const detector = new (getIdleDetectorClass())();
|
|
3213
|
+
detector.addEventListener('change', () => {
|
|
3214
|
+
state.set({
|
|
3215
|
+
user: detector.userState,
|
|
3216
|
+
screen: detector.screenState,
|
|
3217
|
+
});
|
|
3218
|
+
});
|
|
3219
|
+
detector
|
|
3220
|
+
.start({
|
|
3221
|
+
threshold: options.threshold ?? 60_000,
|
|
3222
|
+
signal: abortController.signal,
|
|
3223
|
+
})
|
|
3224
|
+
.catch(() => {
|
|
3225
|
+
/* permission denied or unsupported — keep defaults */
|
|
3226
|
+
});
|
|
3227
|
+
destroyRef.onDestroy(() => abortController.abort());
|
|
3228
|
+
}
|
|
3229
|
+
return {
|
|
3230
|
+
state: state.asReadonly(),
|
|
3231
|
+
userState: computed(() => state().user),
|
|
3232
|
+
screenState: computed(() => state().screen),
|
|
3233
|
+
isUserIdle: computed(() => state().user === 'idle'),
|
|
3234
|
+
isScreenLocked: computed(() => state().screen === 'locked'),
|
|
3235
|
+
};
|
|
3236
|
+
}
|
|
3237
|
+
|
|
3238
|
+
function injectGamepad(index, intervalMs = 16) {
|
|
3239
|
+
const destroyRef = inject(DestroyRef);
|
|
3240
|
+
const platformId = inject(PLATFORM_ID);
|
|
3241
|
+
const state = signal(null, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
3242
|
+
if (isPlatformBrowser(platformId) && isGamepadSupported()) {
|
|
3243
|
+
const sub = gamepadPollStream(index, intervalMs).subscribe((s) => state.set(s));
|
|
3244
|
+
destroyRef.onDestroy(() => sub.unsubscribe());
|
|
3245
|
+
}
|
|
3246
|
+
return {
|
|
3247
|
+
state: state.asReadonly(),
|
|
3248
|
+
connected: computed(() => state()?.connected ?? false),
|
|
3249
|
+
buttons: computed(() => state()?.buttons ?? []),
|
|
3250
|
+
axes: computed(() => state()?.axes ?? []),
|
|
3251
|
+
};
|
|
3252
|
+
}
|
|
3253
|
+
|
|
2331
3254
|
class BrowserSupportUtil {
|
|
2332
3255
|
static isSupported(feature) {
|
|
2333
3256
|
if (typeof window === 'undefined' || typeof navigator === 'undefined') {
|
|
@@ -2466,6 +3389,18 @@ const defaultBrowserWebApisConfig = {
|
|
|
2466
3389
|
enableServerSentEvents: false,
|
|
2467
3390
|
enableVibration: false,
|
|
2468
3391
|
enableSpeechSynthesis: false,
|
|
3392
|
+
enableMutationObserver: false,
|
|
3393
|
+
enablePerformanceObserver: false,
|
|
3394
|
+
enableIdleDetector: false,
|
|
3395
|
+
enableEyeDropper: false,
|
|
3396
|
+
enableBarcodeDetector: false,
|
|
3397
|
+
enableWebAudio: false,
|
|
3398
|
+
enableGamepad: false,
|
|
3399
|
+
enableWebBluetooth: false,
|
|
3400
|
+
enableWebUsb: false,
|
|
3401
|
+
enableWebNfc: false,
|
|
3402
|
+
enablePaymentRequest: false,
|
|
3403
|
+
enableCredentialManagement: false,
|
|
2469
3404
|
};
|
|
2470
3405
|
function provideBrowserWebApis(config = {}) {
|
|
2471
3406
|
const mergedConfig = { ...defaultBrowserWebApisConfig, ...config };
|
|
@@ -2494,6 +3429,18 @@ function provideBrowserWebApis(config = {}) {
|
|
|
2494
3429
|
[mergedConfig.enableServerSentEvents, ServerSentEventsService],
|
|
2495
3430
|
[mergedConfig.enableVibration, VibrationService],
|
|
2496
3431
|
[mergedConfig.enableSpeechSynthesis, SpeechSynthesisService],
|
|
3432
|
+
[mergedConfig.enableMutationObserver, MutationObserverService],
|
|
3433
|
+
[mergedConfig.enablePerformanceObserver, PerformanceObserverService],
|
|
3434
|
+
[mergedConfig.enableIdleDetector, IdleDetectorService],
|
|
3435
|
+
[mergedConfig.enableEyeDropper, EyeDropperService],
|
|
3436
|
+
[mergedConfig.enableBarcodeDetector, BarcodeDetectorService],
|
|
3437
|
+
[mergedConfig.enableWebAudio, WebAudioService],
|
|
3438
|
+
[mergedConfig.enableGamepad, GamepadService],
|
|
3439
|
+
[mergedConfig.enableWebBluetooth, WebBluetoothService],
|
|
3440
|
+
[mergedConfig.enableWebUsb, WebUsbService],
|
|
3441
|
+
[mergedConfig.enableWebNfc, WebNfcService],
|
|
3442
|
+
[mergedConfig.enablePaymentRequest, PaymentRequestService],
|
|
3443
|
+
[mergedConfig.enableCredentialManagement, CredentialManagementService],
|
|
2497
3444
|
];
|
|
2498
3445
|
for (const [enabled, provider] of conditionalProviders) {
|
|
2499
3446
|
if (enabled) {
|
|
@@ -2593,6 +3540,42 @@ function provideVibration() {
|
|
|
2593
3540
|
function provideSpeechSynthesis() {
|
|
2594
3541
|
return makeEnvironmentProviders([SpeechSynthesisService]);
|
|
2595
3542
|
}
|
|
3543
|
+
function provideMutationObserver() {
|
|
3544
|
+
return makeEnvironmentProviders([MutationObserverService]);
|
|
3545
|
+
}
|
|
3546
|
+
function providePerformanceObserver() {
|
|
3547
|
+
return makeEnvironmentProviders([PerformanceObserverService]);
|
|
3548
|
+
}
|
|
3549
|
+
function provideIdleDetector() {
|
|
3550
|
+
return makeEnvironmentProviders([PermissionsService, IdleDetectorService]);
|
|
3551
|
+
}
|
|
3552
|
+
function provideEyeDropper() {
|
|
3553
|
+
return makeEnvironmentProviders([EyeDropperService]);
|
|
3554
|
+
}
|
|
3555
|
+
function provideBarcodeDetector() {
|
|
3556
|
+
return makeEnvironmentProviders([BarcodeDetectorService]);
|
|
3557
|
+
}
|
|
3558
|
+
function provideWebAudio() {
|
|
3559
|
+
return makeEnvironmentProviders([WebAudioService]);
|
|
3560
|
+
}
|
|
3561
|
+
function provideGamepad() {
|
|
3562
|
+
return makeEnvironmentProviders([GamepadService]);
|
|
3563
|
+
}
|
|
3564
|
+
function provideWebBluetooth() {
|
|
3565
|
+
return makeEnvironmentProviders([WebBluetoothService]);
|
|
3566
|
+
}
|
|
3567
|
+
function provideWebUsb() {
|
|
3568
|
+
return makeEnvironmentProviders([WebUsbService]);
|
|
3569
|
+
}
|
|
3570
|
+
function provideWebNfc() {
|
|
3571
|
+
return makeEnvironmentProviders([WebNfcService]);
|
|
3572
|
+
}
|
|
3573
|
+
function providePaymentRequest() {
|
|
3574
|
+
return makeEnvironmentProviders([PaymentRequestService]);
|
|
3575
|
+
}
|
|
3576
|
+
function provideCredentialManagement() {
|
|
3577
|
+
return makeEnvironmentProviders([CredentialManagementService]);
|
|
3578
|
+
}
|
|
2596
3579
|
|
|
2597
3580
|
// Browser Web APIs Services
|
|
2598
3581
|
// Version
|
|
@@ -2602,4 +3585,4 @@ const version = '0.1.0';
|
|
|
2602
3585
|
* Generated bundle index. Do not edit.
|
|
2603
3586
|
*/
|
|
2604
3587
|
|
|
2605
|
-
export { BatteryService, BroadcastChannelService, BrowserApiBaseService, BrowserCapabilityService, BrowserSupportUtil, CameraService, ClipboardService, FileSystemAccessService, FullscreenService, GeolocationService, IntersectionObserverService, MediaDevicesService, MediaRecorderService, NetworkInformationService, NotificationService, PageVisibilityService, PermissionsService, ResizeObserverService, ScreenOrientationService, ScreenWakeLockService, ServerSentEventsService, SpeechSynthesisService, VibrationService, WebShareService, WebSocketService, WebStorageService, WebWorkerService, permissionGuard as createPermissionGuard, defaultBrowserWebApisConfig, injectIntersectionObserver, injectNetworkInformation, injectPageVisibility, injectResizeObserver, injectScreenOrientation, permissionGuard, provideBattery, provideBroadcastChannel, provideBrowserWebApis, provideCamera, provideClipboard, provideCommunicationApis, provideFileSystemAccess, provideFullscreen, provideGeolocation, provideIntersectionObserver, provideLocationApis, provideMediaApis, provideMediaDevices, provideMediaRecorder, provideNetworkInformation, provideNotifications, providePageVisibility, providePermissions, provideResizeObserver, provideScreenOrientation, provideScreenWakeLock, provideServerSentEvents, provideSpeechSynthesis, provideStorageApis, provideVibration, provideWebShare, provideWebSocket, provideWebStorage, provideWebWorker, version };
|
|
3588
|
+
export { BarcodeDetectorService, BatteryService, BroadcastChannelService, BrowserApiBaseService, BrowserCapabilityService, BrowserSupportUtil, CameraService, ClipboardService, ConnectionRegistryBaseService, CredentialManagementService, EyeDropperService, FileSystemAccessService, FullscreenService, GamepadService, GeolocationService, IdleDetectorService, IntersectionObserverService, MediaDevicesService, MediaRecorderService, MutationObserverService, NetworkInformationService, NotificationService, PageVisibilityService, PaymentRequestService, PerformanceObserverService, PermissionAwareBrowserApiBaseService, PermissionsService, ResizeObserverService, ScreenOrientationService, ScreenWakeLockService, ServerSentEventsService, SpeechSynthesisService, VibrationService, WebAudioService, WebBluetoothService, WebNfcService, WebShareService, WebSocketService, WebStorageService, WebUsbService, WebWorkerService, permissionGuard as createPermissionGuard, defaultBrowserWebApisConfig, injectGamepad, injectIdleDetector, injectIntersectionObserver, injectMutationObserver, injectNetworkInformation, injectPageVisibility, injectPerformanceObserver, injectResizeObserver, injectScreenOrientation, permissionGuard, provideBarcodeDetector, provideBattery, provideBroadcastChannel, provideBrowserWebApis, provideCamera, provideClipboard, provideCommunicationApis, provideCredentialManagement, provideEyeDropper, provideFileSystemAccess, provideFullscreen, provideGamepad, provideGeolocation, provideIdleDetector, provideIntersectionObserver, provideLocationApis, provideMediaApis, provideMediaDevices, provideMediaRecorder, provideMutationObserver, provideNetworkInformation, provideNotifications, providePageVisibility, providePaymentRequest, providePerformanceObserver, providePermissions, provideResizeObserver, provideScreenOrientation, provideScreenWakeLock, provideServerSentEvents, provideSpeechSynthesis, provideStorageApis, provideVibration, provideWebAudio, provideWebBluetooth, provideWebNfc, provideWebShare, provideWebSocket, provideWebStorage, provideWebUsb, provideWebWorker, version };
|