@js-toolkit/web-utils 1.67.0 → 1.68.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.
Files changed (116) hide show
  1. package/EventEmitterListener.d.ts +3 -3
  2. package/EventEmitterListener.js +222 -1
  3. package/EventEmitterListener.utils.d.ts +24 -13
  4. package/EventEmitterListener.utils.js +41 -1
  5. package/EventListeners.js +58 -1
  6. package/FullscreenController.js +193 -1
  7. package/README.md +159 -20
  8. package/WakeLockController.js +76 -1
  9. package/base64ToDataUrl.js +3 -1
  10. package/blobToDataUrl.js +10 -1
  11. package/checkPermission.js +8 -1
  12. package/copyToClipboard.js +37 -1
  13. package/createLoop.js +30 -1
  14. package/createRafLoop.js +56 -1
  15. package/dataUrlToBlob.js +13 -1
  16. package/fromBase64.js +10 -1
  17. package/fullscreen.js +167 -1
  18. package/fullscreenUtils.js +37 -1
  19. package/getAspectRatio.js +8 -1
  20. package/getBrowserLanguage.js +10 -1
  21. package/getCurrentScriptUrl.js +4 -1
  22. package/getEventAwaiter.js +41 -1
  23. package/getGeoCoordinates.js +6 -1
  24. package/getGeoLocality.js +19 -1
  25. package/getInnerRect.js +8 -1
  26. package/getInnerXDimensions.js +9 -1
  27. package/getInnerYDimensions.js +9 -1
  28. package/getPinchZoomHandlers.js +134 -1
  29. package/getRandomID.js +4 -1
  30. package/getScreenSize.js +23 -1
  31. package/getSecondsCounter.js +47 -1
  32. package/iframe/getAutoConnector.js +251 -1
  33. package/iframe/getOriginFromMessage.js +3 -1
  34. package/iframe/isIframeLoaded.js +9 -1
  35. package/iframe/messages.d.ts +2 -2
  36. package/iframe/messages.js +50 -1
  37. package/iframe/utils.js +33 -1
  38. package/imageToBlob.js +20 -1
  39. package/isImageTypeSupported.js +8 -1
  40. package/isWebPSupported.js +15 -1
  41. package/loadImage.js +29 -1
  42. package/loadScript.d.ts +1 -1
  43. package/loadScript.js +67 -1
  44. package/media/Capabilities.js +44 -1
  45. package/media/MediaNotAttachedError.d.ts +1 -1
  46. package/media/MediaNotAttachedError.js +6 -1
  47. package/media/MediaStreamController.js +84 -1
  48. package/media/PipController.d.ts +2 -2
  49. package/media/PipController.js +140 -1
  50. package/media/TextTracksController/TextTracksController.d.ts +0 -3
  51. package/media/TextTracksController/TextTracksController.js +251 -1
  52. package/media/TextTracksController/index.js +1 -1
  53. package/media/TextTracksController/utils.js +147 -1
  54. package/media/getDurationTime.js +3 -1
  55. package/media/getMediaSource.js +11 -1
  56. package/media/getSourceBuffer.js +5 -1
  57. package/media/isMediaSeekable.js +4 -1
  58. package/media/parseCueText.js +224 -1
  59. package/media/resetMedia.js +17 -1
  60. package/media/timeRanges.js +9 -1
  61. package/media/toggleNativeSubtitles.js +21 -1
  62. package/metrics/ga/DataLayerProxy.js +11 -1
  63. package/metrics/ga/getHandler.js +99 -1
  64. package/metrics/ga/types.js +1 -1
  65. package/metrics/types.js +1 -1
  66. package/metrics/yandex/DataLayerProxy.js +11 -1
  67. package/metrics/yandex/getHandler.js +63 -1
  68. package/metrics/yandex/types.js +2 -1
  69. package/onDOMReady.js +12 -1
  70. package/onPageReady.js +27 -1
  71. package/package.json +16 -13
  72. package/patchConsoleLogging.js +27 -1
  73. package/performance/getNavigationTiming.js +14 -1
  74. package/platform/Semver.js +23 -1
  75. package/platform/getChromeVersion.d.ts +2 -0
  76. package/platform/getChromeVersion.js +16 -0
  77. package/platform/getIOSVersion.js +18 -1
  78. package/platform/getPlatformInfo.js +49 -1
  79. package/platform/isAirPlayAvailable.js +3 -1
  80. package/platform/isAndroid.js +7 -1
  81. package/platform/isChrome.js +7 -1
  82. package/platform/isEMESupported.js +9 -1
  83. package/platform/isIOS.js +9 -1
  84. package/platform/isMSESupported.js +16 -1
  85. package/platform/isMacOS.js +12 -1
  86. package/platform/isMediaCapabilitiesSupported.js +6 -1
  87. package/platform/isMobile.js +16 -1
  88. package/platform/isMobileSimulation.js +7 -1
  89. package/platform/isSafari.js +14 -1
  90. package/platform/isScreenHDR.js +4 -1
  91. package/platform/isStandaloneApp.js +4 -1
  92. package/platform/isTelegramWebView.js +3 -1
  93. package/platform/isTouchSupported.js +4 -1
  94. package/preventDefault.d.ts +2 -2
  95. package/preventDefault.js +5 -1
  96. package/rafCallback.js +9 -1
  97. package/responsive/MediaQuery.js +40 -1
  98. package/responsive/MediaQueryListener.js +55 -1
  99. package/responsive/ViewSize.js +82 -1
  100. package/responsive/getViewSizeQueryMap.js +7 -1
  101. package/saveFileAs.js +22 -1
  102. package/serviceWorker/ServiceWorkerInstaller.d.ts +0 -1
  103. package/serviceWorker/ServiceWorkerInstaller.js +112 -1
  104. package/serviceWorker/utils.d.ts +1 -1
  105. package/serviceWorker/utils.js +86 -1
  106. package/stopPropagation.d.ts +2 -2
  107. package/stopPropagation.js +3 -1
  108. package/takeScreenshot.js +51 -1
  109. package/toBase64.js +9 -1
  110. package/toLocalPoint.js +9 -1
  111. package/types/index.js +2 -1
  112. package/types/refs.js +1 -1
  113. package/viewableTracker.js +69 -1
  114. package/webrtc/PeerConnection.js +212 -1
  115. package/webrtc/sdputils.js +417 -1
  116. package/ws/WSController.js +148 -1
package/README.md CHANGED
@@ -1,12 +1,11 @@
1
- # Web Utils
1
+ # @js-toolkit/web-utils
2
2
 
3
3
  [![npm package](https://img.shields.io/npm/v/@js-toolkit/web-utils.svg?style=flat-square)](https://www.npmjs.org/package/@js-toolkit/web-utils)
4
+ [![license](https://img.shields.io/npm/l/@js-toolkit/web-utils.svg?style=flat-square)](https://www.npmjs.org/package/@js-toolkit/web-utils)
4
5
 
5
- A lightweight utility library for common operations in the browser environment. This package simplifies tasks such as DOM manipulation, event handling, and other browser-related utilities.
6
+ TypeScript utilities for the browser: media, WebSocket, WebRTC, fullscreen, platform detection, responsive helpers, events, DOM, service workers, and more.
6
7
 
7
- ## Installation
8
-
9
- Install the library via npm:
8
+ ## Install
10
9
 
11
10
  ```bash
12
11
  yarn add @js-toolkit/web-utils
@@ -14,28 +13,168 @@ yarn add @js-toolkit/web-utils
14
13
  npm install @js-toolkit/web-utils
15
14
  ```
16
15
 
17
- # Usage
16
+ ## Import
18
17
 
19
- Here’s an example of how to use the library:
18
+ Use subpath imports for tree-shaking:
20
19
 
21
- ```ts
20
+ ```typescript
21
+ import { FullscreenController } from '@js-toolkit/web-utils/FullscreenController';
22
22
  import { getRandomID } from '@js-toolkit/web-utils/getRandomID';
23
+ import { isMobile } from '@js-toolkit/web-utils/platform/isMobile';
24
+ ```
25
+
26
+ ## API Overview
27
+
28
+ ### Media and Video
29
+
30
+ | Module | Description |
31
+ |--------|-------------|
32
+ | `media/TextTracksController` | HTML5 video text tracks management |
33
+ | `media/PipController` | Picture-in-Picture controller |
34
+ | `media/MediaStreamController` | MediaStream attach/detach |
35
+ | `media/Capabilities` | Media type support detection |
36
+ | `media/timeRanges` | TimeRanges utilities |
37
+ | `media/resetMedia` | Reset media element and clear decoder buffer |
38
+ | `media/getMediaSource` | Get MediaSource or ManagedMediaSource |
39
+ | `media/getSourceBuffer` | Get SourceBuffer or ManagedSourceBuffer |
40
+ | `media/parseCueText` | WebVTT cue text parser |
41
+
42
+ ### WebSocket and WebRTC
43
+
44
+ | Module | Description |
45
+ |--------|-------------|
46
+ | `ws/WSController` | WebSocket controller with auto-reconnect |
47
+ | `webrtc/PeerConnection` | RTCPeerConnection wrapper |
48
+ | `webrtc/sdputils` | SDP manipulation utilities |
49
+
50
+ ### Fullscreen
51
+
52
+ | Module | Description |
53
+ |--------|-------------|
54
+ | `FullscreenController` | Fullscreen API controller with fallback |
55
+ | `fullscreen` | Cross-browser fullscreen utilities |
56
+ | `fullscreenUtils` | Pseudo-fullscreen helpers |
57
+
58
+ ### Platform Detection
59
+
60
+ | Module | Description |
61
+ |--------|-------------|
62
+ | `platform/getPlatformInfo` | Browser/platform detection (ua-parser-js) |
63
+ | `platform/isSafari`, `isChrome` | Browser detection |
64
+ | `platform/isIOS`, `isAndroid`, `isMacOS` | OS detection |
65
+ | `platform/isMobile` | Mobile device detection |
66
+ | `platform/isTouchSupported` | Touch support detection |
67
+ | `platform/isMSESupported` | Media Source Extensions support |
68
+ | `platform/isEMESupported` | Encrypted Media Extensions support |
69
+ | `platform/isAirPlayAvailable` | AirPlay availability |
70
+ | `platform/Semver` | Semantic version parser |
71
+
72
+ ### Responsive
73
+
74
+ | Module | Description |
75
+ |--------|-------------|
76
+ | `responsive/MediaQueryListener` | Media query change listener |
77
+ | `responsive/MediaQuery` | Static media query utilities |
78
+ | `responsive/ViewSize` | Viewport size enum and helpers |
79
+
80
+ ### Events
81
+
82
+ | Module | Description |
83
+ |--------|-------------|
84
+ | `EventEmitterListener` | Unified listener for DOM/EventEmitter/EventTarget |
85
+ | `EventListeners` | Multi-target event listener manager |
86
+ | `getEventAwaiter` | Promise-based event waiting |
87
+
88
+ ### DOM
89
+
90
+ | Module | Description |
91
+ |--------|-------------|
92
+ | `onDOMReady` | Execute callback when DOM is ready |
93
+ | `onPageReady` | Execute callback when page is loaded |
94
+ | `getInnerRect` | Element inner dimensions |
95
+ | `toLocalPoint` | Convert coordinates to local element space |
96
+ | `loadScript` | Dynamic script loading with deduplication |
97
+ | `copyToClipboard` | Copy text to clipboard |
98
+
99
+ ### Files and Blobs
100
+
101
+ | Module | Description |
102
+ |--------|-------------|
103
+ | `saveFileAs` | Trigger file download |
104
+ | `loadImage` | Load image with caching |
105
+ | `blobToDataUrl`, `dataUrlToBlob` | Blob/DataURL conversions |
106
+ | `toBase64`, `fromBase64` | Unicode-safe base64 encoding |
107
+ | `takeScreenshot` | Capture screenshot from CanvasImageSource |
23
108
 
24
- const id = getRandomID();
109
+ ### Animation
110
+
111
+ | Module | Description |
112
+ |--------|-------------|
113
+ | `createLoop` | Interval-based loop with RAF throttling |
114
+ | `createRafLoop` | RequestAnimationFrame loop |
115
+
116
+ ### Metrics
117
+
118
+ | Module | Description |
119
+ |--------|-------------|
120
+ | `metrics/ga/*` | Google Analytics helpers |
121
+ | `metrics/yandex/*` | Yandex Metrica helpers |
122
+
123
+ ### Other
124
+
125
+ | Module | Description |
126
+ |--------|-------------|
127
+ | `getRandomID` | Random ID via crypto API |
128
+ | `getBrowserLanguage` | Browser language detection |
129
+ | `WakeLockController` | Screen wake lock |
130
+ | `serviceWorker/ServiceWorkerInstaller` | Service worker registration |
131
+ | `iframe/*` | Iframe parent-child communication |
132
+ | `viewableTracker` | Element visibility tracking |
133
+
134
+ ## Usage Examples
135
+
136
+ ### FullscreenController
137
+
138
+ ```typescript
139
+ import { FullscreenController } from '@js-toolkit/web-utils/FullscreenController';
140
+
141
+ const controller = new FullscreenController(document.getElementById('player')!);
142
+ await controller.requestFullscreen();
143
+ console.log(controller.isFullscreen);
25
144
  ```
26
145
 
27
- # Features
146
+ ### Platform detection
28
147
 
29
- - Simplifies common browser tasks
30
- - Lightweight and fast
31
- - Easy to integrate with other JavaScript projects
148
+ ```typescript
149
+ import { isMobile } from '@js-toolkit/web-utils/platform/isMobile';
150
+ import { isSafari } from '@js-toolkit/web-utils/platform/isSafari';
32
151
 
33
- # Contributing
152
+ if (isMobile()) {
153
+ // mobile-specific logic
154
+ }
155
+ if (isSafari()) {
156
+ // Safari workaround
157
+ }
158
+ ```
159
+
160
+ ### Event awaiter
161
+
162
+ ```typescript
163
+ import { getEventAwaiter } from '@js-toolkit/web-utils/getEventAwaiter';
164
+
165
+ const video = document.querySelector('video')!;
166
+ await getEventAwaiter(video, 'canplay').wait();
167
+ video.play();
168
+ ```
169
+
170
+ ### Dynamic script loading
171
+
172
+ ```typescript
173
+ import { loadScript } from '@js-toolkit/web-utils/loadScript';
174
+
175
+ await loadScript('https://cdn.example.com/sdk.js');
176
+ ```
34
177
 
35
- Contributions are welcome! Please follow these steps:
178
+ ## Repository
36
179
 
37
- 1. Fork the repository.
38
- 1. Create a new branch (git checkout -b feature/my-feature-name).
39
- 1. Commit your changes (git commit -m 'Add some feature').
40
- 1. Push the branch (git push origin feature/my-feature-name).
41
- 1. Open a pull request.
180
+ [https://github.com/js-toolkit/web-utils](https://github.com/js-toolkit/web-utils)
@@ -1 +1,76 @@
1
- import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{hasIn}from"@js-toolkit/utils/hasIn";export class WakeLockController extends EventEmitter{static isApiAvailable(){return hasIn(navigator,"wakeLock")&&null!=navigator.wakeLock}wakelock;releasing=!1;relockOnVisible=!1;restoreWakeLock=()=>{"visible"===document.visibilityState&&this.relockOnVisible&&(this.relockOnVisible=!1,this.request().catch(e=>this.emit("error",{error:e})))};onRelease=()=>{this.wakelock&&(this.wakelock.removeEventListener("release",this.onRelease),this.wakelock=void 0,this.releasing?document.removeEventListener("visibilitychange",this.restoreWakeLock):"hidden"===document.visibilityState&&(this.relockOnVisible=!0,document.addEventListener("visibilitychange",this.restoreWakeLock)),this.emit("deactivated"))};isActive(){return!!this.wakelock&&!this.wakelock.released}async request(){if(!this.wakelock){try{this.wakelock=await navigator.wakeLock.request("screen")}catch(e){if("visible"===document.visibilityState)throw e;return this.relockOnVisible=!0,document.addEventListener("visibilitychange",this.restoreWakeLock),void this.emit("error",{error:e})}this.relockOnVisible=!1,this.wakelock.addEventListener("release",this.onRelease),this.emit("activated")}}async release(){if(this.wakelock&&!this.wakelock.released)try{this.releasing=!0,await this.wakelock.release(),this.relockOnVisible=!1}finally{this.releasing=!1}}async destroy(){await this.release(),this.removeAllListeners()}[Symbol.asyncDispose](){return this.destroy()}}
1
+ import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
2
+ import { hasIn } from '@js-toolkit/utils/hasIn';
3
+ export class WakeLockController extends EventEmitter {
4
+ static isApiAvailable() {
5
+ return hasIn(navigator, 'wakeLock') && navigator.wakeLock != null;
6
+ }
7
+ wakelock;
8
+ releasing = false;
9
+ /** Lock when document will be visible. */
10
+ relockOnVisible = false;
11
+ restoreWakeLock = () => {
12
+ if (document.visibilityState === 'visible' && this.relockOnVisible) {
13
+ this.relockOnVisible = false;
14
+ this.request().catch((error) => this.emit('error', { error }));
15
+ }
16
+ };
17
+ onRelease = () => {
18
+ if (!this.wakelock)
19
+ return;
20
+ this.wakelock.removeEventListener('release', this.onRelease);
21
+ this.wakelock = undefined;
22
+ // Unsubscribe only if document is visible else keep subscription to re-lock when document will be visible again.
23
+ // Release the wakelock happens before `visibilitychange` callback.
24
+ if (this.releasing) {
25
+ document.removeEventListener('visibilitychange', this.restoreWakeLock);
26
+ }
27
+ // If not releasing via api
28
+ else if (document.visibilityState === 'hidden') {
29
+ this.relockOnVisible = true;
30
+ document.addEventListener('visibilitychange', this.restoreWakeLock);
31
+ }
32
+ this.emit('deactivated');
33
+ };
34
+ isActive() {
35
+ return !!this.wakelock && !this.wakelock.released;
36
+ }
37
+ async request() {
38
+ if (this.wakelock)
39
+ return;
40
+ try {
41
+ this.wakelock = await navigator.wakeLock.request('screen');
42
+ }
43
+ catch (error) {
44
+ // Probably do not have permissions.
45
+ if (document.visibilityState === 'visible')
46
+ throw error;
47
+ // If trying to wakelock when document inactive.
48
+ this.relockOnVisible = true;
49
+ document.addEventListener('visibilitychange', this.restoreWakeLock);
50
+ this.emit('error', { error });
51
+ return;
52
+ }
53
+ this.relockOnVisible = false;
54
+ this.wakelock.addEventListener('release', this.onRelease);
55
+ this.emit('activated');
56
+ }
57
+ async release() {
58
+ if (!this.wakelock || this.wakelock.released)
59
+ return;
60
+ try {
61
+ this.releasing = true;
62
+ await this.wakelock.release();
63
+ this.relockOnVisible = false;
64
+ }
65
+ finally {
66
+ this.releasing = false;
67
+ }
68
+ }
69
+ async destroy() {
70
+ await this.release();
71
+ this.removeAllListeners();
72
+ }
73
+ [Symbol.asyncDispose]() {
74
+ return this.destroy();
75
+ }
76
+ }
@@ -1 +1,3 @@
1
- export function base64ToDataUrl(a,e="image/png"){return`data:${e};base64,${a}`}
1
+ export function base64ToDataUrl(base64, type = 'image/png') {
2
+ return `data:${type};base64,${base64}`;
3
+ }
package/blobToDataUrl.js CHANGED
@@ -1 +1,10 @@
1
- export function blobToDataUrl(e){return new Promise((r,o)=>{const n=new FileReader;n.onload=()=>{r(n.result)},n.onerror=o,n.readAsDataURL(e)})}
1
+ export function blobToDataUrl(blob) {
2
+ return new Promise((resolve, reject) => {
3
+ const reader = new FileReader();
4
+ reader.onload = () => {
5
+ resolve(reader.result);
6
+ };
7
+ reader.onerror = reject;
8
+ reader.readAsDataURL(blob);
9
+ });
10
+ }
@@ -1 +1,8 @@
1
- export async function checkPermission(t){try{return(await(navigator.permissions?.query?.({name:t}))).state}catch{}}
1
+ export async function checkPermission(name) {
2
+ try {
3
+ const result = await navigator.permissions?.query?.({ name });
4
+ return result.state;
5
+ }
6
+ catch { }
7
+ return undefined;
8
+ }
@@ -1 +1,37 @@
1
- export function copyToClipboard(e){return navigator.clipboard?navigator.clipboard.writeText(e):new Promise((o,t)=>{const n=document.createElement("textarea");n.textContent=e,n.style.position="fixed",n.style.bottom="-100px",n.readOnly=!0,document.body.appendChild(n);const c=document.createRange();c.selectNode(n);const a=window.getSelection();if(a){a.removeAllRanges(),a.addRange(c),n.setSelectionRange(0,999999);try{document.execCommand("copy",!1),o()}catch(e){t(e)}finally{a.removeAllRanges(),document.body.removeChild(n)}}else t(new Error("No selection for copy to clipboard."))})}
1
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
2
+ /* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
3
+ /* eslint-disable @typescript-eslint/no-deprecated */
4
+ export function copyToClipboard(text) {
5
+ if (navigator.clipboard) {
6
+ return navigator.clipboard.writeText(text);
7
+ }
8
+ return new Promise((resolve, reject) => {
9
+ const textarea = document.createElement('textarea');
10
+ textarea.textContent = text;
11
+ textarea.style.position = 'fixed';
12
+ textarea.style.bottom = '-100px';
13
+ textarea.readOnly = true; // To avoid showing keyboard;
14
+ document.body.appendChild(textarea);
15
+ const range = document.createRange();
16
+ range.selectNode(textarea);
17
+ const selection = window.getSelection();
18
+ if (!selection) {
19
+ reject(new Error('No selection for copy to clipboard.'));
20
+ return;
21
+ }
22
+ selection.removeAllRanges();
23
+ selection.addRange(range);
24
+ textarea.setSelectionRange(0, 999999);
25
+ try {
26
+ document.execCommand('copy', false);
27
+ resolve();
28
+ }
29
+ catch (err) {
30
+ reject(err);
31
+ }
32
+ finally {
33
+ selection.removeAllRanges();
34
+ document.body.removeChild(textarea);
35
+ }
36
+ });
37
+ }
package/createLoop.js CHANGED
@@ -1 +1,30 @@
1
- export function createLoop(){let e,t,n,a=!1;const r=()=>{a&&n&&(t&&cancelAnimationFrame(t),t=requestAnimationFrame(n))};return{start:(t,i)=>{n=t,a||(a=!0,e=window.setInterval(r,i))},stop:()=>{a&&(a=!1,window.clearInterval(e),t&&cancelAnimationFrame(t))},call:r,isActive:()=>a}}
1
+ export function createLoop() {
2
+ let timer;
3
+ let raf;
4
+ let active = false;
5
+ let rafCallback;
6
+ const call = () => {
7
+ if (active && rafCallback) {
8
+ raf != null && cancelAnimationFrame(raf);
9
+ raf = requestAnimationFrame(rafCallback);
10
+ }
11
+ };
12
+ return {
13
+ start: (callback, wait) => {
14
+ rafCallback = callback;
15
+ if (!active) {
16
+ active = true;
17
+ timer = window.setInterval(call, wait);
18
+ }
19
+ },
20
+ stop: () => {
21
+ if (active) {
22
+ active = false;
23
+ window.clearInterval(timer);
24
+ raf != null && cancelAnimationFrame(raf);
25
+ }
26
+ },
27
+ call,
28
+ isActive: () => active,
29
+ };
30
+ }
package/createRafLoop.js CHANGED
@@ -1 +1,56 @@
1
- export function createRafLoop(){let e,t,o,i=!1,n=0,a=window;const r=()=>{a.clearTimeout(e),t&&a.cancelAnimationFrame(t),t=void 0,n=0,e=void 0},s=()=>{t=a.requestAnimationFrame(u)},u=u=>{o&&(i?(o(u),n>0?e=a.setTimeout(s,n):s()):null==t&&null==e||(r(),o(u)))};return{start:(e,{suspendTimeout:t=0,scope:r=window}={})=>{o=e,n=t,a=r,i||(i=!0,s())},stop:e=>{i&&(i=!1,e||r())},isActive:()=>i}}
1
+ export function createRafLoop() {
2
+ let active = false;
3
+ let timer;
4
+ let raf;
5
+ let suspendTimeout = 0;
6
+ let scope = window;
7
+ let rafCallback;
8
+ const reset = () => {
9
+ scope.clearTimeout(timer);
10
+ raf != null && scope.cancelAnimationFrame(raf);
11
+ raf = undefined;
12
+ suspendTimeout = 0;
13
+ timer = undefined;
14
+ };
15
+ const call = () => {
16
+ // eslint-disable-next-line @typescript-eslint/no-use-before-define
17
+ raf = scope.requestAnimationFrame(step);
18
+ };
19
+ const step = (time) => {
20
+ if (!rafCallback)
21
+ return;
22
+ if (active) {
23
+ rafCallback(time);
24
+ if (suspendTimeout > 0) {
25
+ timer = scope.setTimeout(call, suspendTimeout);
26
+ }
27
+ else {
28
+ call();
29
+ }
30
+ }
31
+ // Wait last call
32
+ else if (raf != null || timer != null) {
33
+ reset();
34
+ rafCallback(time);
35
+ }
36
+ };
37
+ return {
38
+ start: (callback, { suspendTimeout: _suspendTimeout = 0, scope: _scope = window } = {}) => {
39
+ rafCallback = callback;
40
+ suspendTimeout = _suspendTimeout;
41
+ scope = _scope;
42
+ if (!active) {
43
+ active = true;
44
+ call();
45
+ }
46
+ },
47
+ stop: (waitLast) => {
48
+ if (active) {
49
+ active = false;
50
+ if (!waitLast)
51
+ reset();
52
+ }
53
+ },
54
+ isActive: () => active,
55
+ };
56
+ }
package/dataUrlToBlob.js CHANGED
@@ -1 +1,13 @@
1
- export function dataUrlToBlob(t){const[e,n]=t.split(","),r=window.atob(n),o=e.split(":")[1].split(";")[0],l=new ArrayBuffer(r.length),a=new Uint8Array(l),{length:i}=r;for(let t=0;t<i;t+=1)a[t]=r.charCodeAt(t);return new Blob([l],{type:o})}
1
+ // https://stackoverflow.com/a/12300351
2
+ export function dataUrlToBlob(dataUrl) {
3
+ const [type, base64] = dataUrl.split(',');
4
+ const byteString = window.atob(base64);
5
+ const mimeString = type.split(':')[1].split(';')[0];
6
+ const ab = new ArrayBuffer(byteString.length);
7
+ const ia = new Uint8Array(ab);
8
+ const { length } = byteString;
9
+ for (let i = 0; i < length; i += 1) {
10
+ ia[i] = byteString.charCodeAt(i);
11
+ }
12
+ return new Blob([ab], { type: mimeString });
13
+ }
package/fromBase64.js CHANGED
@@ -1 +1,10 @@
1
- export function fromBase64(e){const o=window.atob(e);return window.TextDecoder?(new TextDecoder).decode(Uint8Array.from(o,e=>e.codePointAt(0))):decodeURIComponent(escape(o))}
1
+ /* eslint-disable @typescript-eslint/no-non-null-assertion */
2
+ /* eslint-disable @typescript-eslint/no-deprecated */
3
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
4
+ /** Safe for unicode string. */
5
+ export function fromBase64(str) {
6
+ const binString = window.atob(str);
7
+ return window.TextDecoder
8
+ ? new TextDecoder().decode(Uint8Array.from(binString, (m) => m.codePointAt(0)))
9
+ : decodeURIComponent(escape(binString));
10
+ }
package/fullscreen.js CHANGED
@@ -1 +1,167 @@
1
- import{es5ErrorCompat}from"@js-toolkit/utils/es5ErrorCompat";import{promisify}from"@js-toolkit/utils/promisify";export class FullscreenUnavailableError extends Error{constructor(){super("Fullscreen is not available"),es5ErrorCompat(this,FullscreenUnavailableError)}}export var fullscreen;!function(e){e.names=[{requestFullscreenName:"requestFullscreen",exitFullscreenName:"exitFullscreen",fullscreenElementName:"fullscreenElement",fullscreenEnabledName:"fullscreenEnabled",changeEventName:"fullscreenchange",errorEventName:"fullscreenerror"},{requestFullscreenName:"webkitRequestFullscreen",exitFullscreenName:"webkitExitFullscreen",fullscreenElementName:"webkitFullscreenElement",fullscreenEnabledName:"webkitFullscreenEnabled",changeEventName:"webkitfullscreenchange",errorEventName:"webkitfullscreenerror"},{requestFullscreenName:"webkitRequestFullScreen",exitFullscreenName:"webkitCancelFullScreen",fullscreenElementName:"webkitCurrentFullScreenElement",fullscreenEnabledName:"webkitCancelFullScreen",changeEventName:"webkitfullscreenchange",errorEventName:"webkitfullscreenerror"},{requestFullscreenName:"mozRequestFullScreen",exitFullscreenName:"mozCancelFullScreen",fullscreenElementName:"mozFullScreenElement",fullscreenEnabledName:"mozFullScreenEnabled",changeEventName:"mozfullscreenchange",errorEventName:"mozfullscreenerror"},{requestFullscreenName:"msRequestFullscreen",exitFullscreenName:"msExitFullscreen",fullscreenElementName:"msFullscreenElement",fullscreenEnabledName:"msFullscreenEnabled",changeEventName:"MSFullscreenChange",errorEventName:"MSFullscreenError"}].find(({exitFullscreenName:e})=>e in document);const n={change:e.names?.changeEventName,error:e.names?.errorEventName};function r(){if(!e.names)throw new e.UnavailableError;return Boolean(document[e.names.fullscreenElementName])}function l(e,r,l){const a=n[e];a&&document.addEventListener(a,r,l)}function a(e,r,l){const a=n[e];a&&document.removeEventListener(a,r,l)}function t(n,r){return new Promise((t,s)=>{if(!e.names)throw new e.UnavailableError;const c=()=>{a("change",c),a("error",u),t()},u=e=>{a("change",c),a("error",u),s(e)};l("change",c),l("error",u),n[e.names.requestFullscreenName](r)})}function s(){return new Promise((n,t)=>{if(!e.names)throw new e.UnavailableError;if(!r())return void n();const s=()=>{a("change",s),a("error",c),n()},c=e=>{a("change",s),a("error",c),t(e)};l("change",s),l("error",c),document[e.names.exitFullscreenName]()})}e.UnavailableError=FullscreenUnavailableError,e.isApiAvailable=function(){return!!e.names},e.isApiEnabled=function(){return!!e.names&&Boolean(document[e.names.fullscreenEnabledName])},e.isFullscreen=r,e.getElement=function(){if(!e.names)throw new e.UnavailableError;return document[e.names.fullscreenElementName]},e.on=l,e.off=a,e.request=t,e.exit=s,e.toggle=function(e){return promisify(()=>r()?s():t(e))},e.onChange=function(e){l("change",e)},e.onError=function(e){l("error",e)}}(fullscreen||(fullscreen={}));
1
+ /* eslint-disable @typescript-eslint/no-namespace */
2
+ /* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
3
+ /* eslint-disable @typescript-eslint/no-use-before-define */
4
+ import { es5ErrorCompat } from '@js-toolkit/utils/es5ErrorCompat';
5
+ import { promisify } from '@js-toolkit/utils/promisify';
6
+ export class FullscreenUnavailableError extends Error {
7
+ constructor() {
8
+ super('Fullscreen is not available');
9
+ es5ErrorCompat(this, FullscreenUnavailableError);
10
+ }
11
+ }
12
+ export var fullscreen;
13
+ (function (fullscreen) {
14
+ fullscreen.names = (() => {
15
+ const fnNames = [
16
+ {
17
+ requestFullscreenName: 'requestFullscreen',
18
+ exitFullscreenName: 'exitFullscreen',
19
+ fullscreenElementName: 'fullscreenElement',
20
+ fullscreenEnabledName: 'fullscreenEnabled',
21
+ changeEventName: 'fullscreenchange',
22
+ errorEventName: 'fullscreenerror',
23
+ },
24
+ // New WebKit
25
+ {
26
+ requestFullscreenName: 'webkitRequestFullscreen',
27
+ exitFullscreenName: 'webkitExitFullscreen',
28
+ fullscreenElementName: 'webkitFullscreenElement',
29
+ fullscreenEnabledName: 'webkitFullscreenEnabled',
30
+ changeEventName: 'webkitfullscreenchange',
31
+ errorEventName: 'webkitfullscreenerror',
32
+ },
33
+ // Old WebKit
34
+ {
35
+ requestFullscreenName: 'webkitRequestFullScreen',
36
+ exitFullscreenName: 'webkitCancelFullScreen',
37
+ fullscreenElementName: 'webkitCurrentFullScreenElement',
38
+ fullscreenEnabledName: 'webkitCancelFullScreen',
39
+ changeEventName: 'webkitfullscreenchange',
40
+ errorEventName: 'webkitfullscreenerror',
41
+ },
42
+ {
43
+ requestFullscreenName: 'mozRequestFullScreen',
44
+ exitFullscreenName: 'mozCancelFullScreen',
45
+ fullscreenElementName: 'mozFullScreenElement',
46
+ fullscreenEnabledName: 'mozFullScreenEnabled',
47
+ changeEventName: 'mozfullscreenchange',
48
+ errorEventName: 'mozfullscreenerror',
49
+ },
50
+ {
51
+ requestFullscreenName: 'msRequestFullscreen',
52
+ exitFullscreenName: 'msExitFullscreen',
53
+ fullscreenElementName: 'msFullscreenElement',
54
+ fullscreenEnabledName: 'msFullscreenEnabled',
55
+ changeEventName: 'MSFullscreenChange',
56
+ errorEventName: 'MSFullscreenError',
57
+ },
58
+ ];
59
+ return fnNames.find(({ exitFullscreenName }) => exitFullscreenName in document);
60
+ })();
61
+ const eventNameMap = {
62
+ change: fullscreen.names?.changeEventName,
63
+ error: fullscreen.names?.errorEventName,
64
+ };
65
+ fullscreen.UnavailableError = FullscreenUnavailableError;
66
+ function isApiAvailable() {
67
+ return !!fullscreen.names;
68
+ }
69
+ fullscreen.isApiAvailable = isApiAvailable;
70
+ function isApiEnabled() {
71
+ // Coerce to boolean in case of old WebKit
72
+ return !!fullscreen.names && Boolean(document[fullscreen.names.fullscreenEnabledName]);
73
+ }
74
+ fullscreen.isApiEnabled = isApiEnabled;
75
+ function isFullscreen() {
76
+ if (!fullscreen.names)
77
+ throw new fullscreen.UnavailableError();
78
+ return Boolean(document[fullscreen.names.fullscreenElementName]);
79
+ }
80
+ fullscreen.isFullscreen = isFullscreen;
81
+ function getElement() {
82
+ if (!fullscreen.names)
83
+ throw new fullscreen.UnavailableError();
84
+ return document[fullscreen.names.fullscreenElementName];
85
+ }
86
+ fullscreen.getElement = getElement;
87
+ function on(type, listener, options) {
88
+ const eventName = eventNameMap[type];
89
+ if (eventName)
90
+ document.addEventListener(eventName, listener, options);
91
+ }
92
+ fullscreen.on = on;
93
+ function off(type, listener, options) {
94
+ const eventName = eventNameMap[type];
95
+ if (eventName)
96
+ document.removeEventListener(eventName, listener, options);
97
+ }
98
+ fullscreen.off = off;
99
+ function request(elem, options) {
100
+ return new Promise((resolve, reject) => {
101
+ if (!fullscreen.names) {
102
+ throw new fullscreen.UnavailableError();
103
+ }
104
+ const onFullScreenEntered = () => {
105
+ off('change', onFullScreenEntered);
106
+ off('error', onFullScreenError);
107
+ resolve();
108
+ };
109
+ const onFullScreenError = (event) => {
110
+ off('change', onFullScreenEntered);
111
+ off('error', onFullScreenError);
112
+ reject(event);
113
+ };
114
+ on('change', onFullScreenEntered);
115
+ on('error', onFullScreenError);
116
+ void elem[fullscreen.names.requestFullscreenName](options);
117
+ // const result = ((elem as AnyObject)[names.requestFullscreenName] as AnyAsyncFunction)(
118
+ // options
119
+ // );
120
+ // if (result instanceof Promise) {
121
+ // result.then(onFullScreenEntered).catch(onFullScreenError);
122
+ // }
123
+ });
124
+ }
125
+ fullscreen.request = request;
126
+ function exit() {
127
+ return new Promise((resolve, reject) => {
128
+ if (!fullscreen.names) {
129
+ throw new fullscreen.UnavailableError();
130
+ }
131
+ if (!isFullscreen()) {
132
+ resolve();
133
+ return;
134
+ }
135
+ const onFullScreenExited = () => {
136
+ off('change', onFullScreenExited);
137
+ off('error', onFullScreenError);
138
+ resolve();
139
+ };
140
+ const onFullScreenError = (event) => {
141
+ off('change', onFullScreenExited);
142
+ off('error', onFullScreenError);
143
+ reject(event);
144
+ };
145
+ on('change', onFullScreenExited);
146
+ on('error', onFullScreenError);
147
+ void document[fullscreen.names.exitFullscreenName]();
148
+ // const result = ((document as AnyObject)[names.exitFullscreenName] as AnyAsyncFunction)();
149
+ // if (result instanceof Promise) {
150
+ // result.then(onFullScreenExited).catch(onFullScreenError);
151
+ // }
152
+ });
153
+ }
154
+ fullscreen.exit = exit;
155
+ function toggle(elem) {
156
+ return promisify(() => (isFullscreen() ? exit() : request(elem)));
157
+ }
158
+ fullscreen.toggle = toggle;
159
+ function onChange(listener) {
160
+ on('change', listener);
161
+ }
162
+ fullscreen.onChange = onChange;
163
+ function onError(listener) {
164
+ on('error', listener);
165
+ }
166
+ fullscreen.onError = onError;
167
+ })(fullscreen || (fullscreen = {}));