@js-toolkit/web-utils 1.66.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 (120) 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 +19 -16
  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.d.ts +18 -0
  98. package/responsive/MediaQuery.js +40 -0
  99. package/responsive/MediaQueryListener.d.ts +19 -0
  100. package/responsive/MediaQueryListener.js +55 -0
  101. package/responsive/ViewSize.d.ts +45 -0
  102. package/responsive/ViewSize.js +82 -0
  103. package/responsive/getViewSizeQueryMap.d.ts +2 -0
  104. package/responsive/getViewSizeQueryMap.js +7 -0
  105. package/saveFileAs.js +22 -1
  106. package/serviceWorker/ServiceWorkerInstaller.d.ts +0 -1
  107. package/serviceWorker/ServiceWorkerInstaller.js +112 -1
  108. package/serviceWorker/utils.d.ts +1 -1
  109. package/serviceWorker/utils.js +86 -1
  110. package/stopPropagation.d.ts +2 -2
  111. package/stopPropagation.js +3 -1
  112. package/takeScreenshot.js +51 -1
  113. package/toBase64.js +9 -1
  114. package/toLocalPoint.js +9 -1
  115. package/types/index.js +2 -1
  116. package/types/refs.js +1 -1
  117. package/viewableTracker.js +69 -1
  118. package/webrtc/PeerConnection.js +212 -1
  119. package/webrtc/sdputils.js +417 -1
  120. package/ws/WSController.js +148 -1
@@ -1,7 +1,7 @@
1
1
  import { type DomEventTarget, type GetEventMap, type EmitterTarget, type GetEventType, type GetEventListener } from './EventEmitterListener.utils';
2
- type GetOnOptions<T extends EmitterTarget> = T extends DomEventTarget ? boolean | AddEventListenerOptions : unknown;
3
- type GetOnceOptions<T extends EmitterTarget> = T extends DomEventTarget ? boolean | OmitStrict<AddEventListenerOptions, 'once'> : undefined;
4
- type GetOffOptions<T extends EmitterTarget> = T extends DomEventTarget ? boolean | EventListenerOptions : undefined;
2
+ type GetOnOptions<T> = T extends DomEventTarget ? boolean | AddEventListenerOptions : unknown;
3
+ type GetOnceOptions<T> = T extends DomEventTarget ? boolean | OmitStrict<AddEventListenerOptions, 'once'> : undefined;
4
+ type GetOffOptions<T> = T extends DomEventTarget ? boolean | EventListenerOptions : undefined;
5
5
  interface GetListenersOptions {
6
6
  event?: string | undefined;
7
7
  type?: 'normal' | 'capture' | undefined;
@@ -1 +1,222 @@
1
- import{isPassiveSupported,isDomEventTarget,isEventTargetLike,normalizeOptions}from"./EventEmitterListener.utils";function getEventTypeError(){return new Error("Event type can not be null.")}function getEventListenerError(){return new Error("Event listener can not be null.")}export class EventEmitterListener{target;interceptor;normalListeners={};captureListeners={};passiveSupported=isPassiveSupported();constructor(e,t){this.target=e,this.interceptor=t,this.target=e}createWrapper(e,t,r,...s){return(...n)=>{r&&this.off(e,t,...s);const i=this.interceptor&&this.interceptor(...n)||n;t(...i)}}getListenerList({event:e,type:t,wrapper:r}={}){const s="normal"===t&&this.normalListeners||"capture"===t&&this.captureListeners||void 0;if(s){const t=e?s[e]&&{[e]:s[e]}:s;return t?Object.values(t).flatMap(e=>e?Array.from(r?e.values():e.keys()):[]):[]}return this.getListenerList({event:e,type:"normal",wrapper:r}).concat(this.getListenerList({event:e,type:"capture",wrapper:r}))}getListeners({event:e,type:t,wrapper:r}={}){const s="normal"===t&&this.normalListeners||"capture"===t&&this.captureListeners||void 0;if(!s){const t=this.getListeners({event:e,type:"normal",wrapper:r}),s=this.getListeners({event:e,type:"capture",wrapper:r}),n={},i=([e,t])=>{const r=n[e];n[e]=r?r.concat(t):t};return Object.entries(t).forEach(i),Object.entries(s).forEach(i),n}const n=e?s[e]&&{[e]:s[e]}:s;return n?Object.entries(n).reduce((e,[t,s])=>{const n=s?Array.from(r?s.values():s.keys()):[];return n.length>0&&(e[t]=n),e},{}):{}}has(e,t,...r){if(!isDomEventTarget(this.target)){const r=this.normalListeners[e];return!!r&&(t?r.has(t):r.size>0)}const s=r[0],n=!0===s||s&&"object"==typeof s&&(s.capture??!1)?this.captureListeners[e]:this.normalListeners[e];return!!n&&(t?n.has(t):n.size>0)}on(e,t,...r){if(null==e)throw getEventTypeError();if(null==t)throw getEventListenerError();if(!isDomEventTarget(this.target)){const s=this.normalListeners[e]??new Map;this.normalListeners[e]=s;const n=s.get(t)??(this.interceptor?this.createWrapper(e,t,!1,...r):t);return!s.has(t)&&s.set(t,n),isEventTargetLike(this.target)?this.target.addEventListener(e,n,...r):this.target.on(e,n,...r),this}const s=r[0],n=!0===s||s&&"object"==typeof s&&(s.capture??!1),i=(s&&"object"==typeof s&&s.once)??!1;if(n){const n=this.captureListeners[e]??new Map;this.captureListeners[e]=n;const o=n.get(t)??this.createWrapper(e,t,i,...r);!n.has(t)&&n.set(t,o),this.target.addEventListener(e,o,normalizeOptions(s))}else{const n=this.normalListeners[e]??new Map;this.normalListeners[e]=n;const o=n.get(t)??this.createWrapper(e,t,i,...r);!n.has(t)&&n.set(t,o),this.target.addEventListener(e,o,normalizeOptions(s))}return this}once(e,t,...r){if(null==e)throw getEventTypeError();if(null==t)throw getEventListenerError();if(!isDomEventTarget(this.target)){const s=this.normalListeners[e]??new Map;this.normalListeners[e]=s;const n=s.get(t)??this.createWrapper(e,t,!0,...r);return!s.has(t)&&s.set(t,n),isEventTargetLike(this.target)?this.target.addEventListener(e,n,...r):this.target.once?this.target.once(e,n,...r):this.target.on(e,n,...r),this}const s=r[0];return this.on(e,t,{..."object"==typeof s?s:null!=s&&{capture:s},once:!0})}off(e,t,...r){if(!isDomEventTarget(this.target)){const s=this.normalListeners[e],n=s?.get(t);return s&&n&&s.delete(t),0===s?.size&&delete this.normalListeners[e],isEventTargetLike(this.target)?this.target.removeEventListener(e,n??t,...r):this.target.off(e,n??t,...r),this}const s=r[0],n=!0===s||s&&"object"==typeof s&&(s.capture??!1),i=n?this.captureListeners[e]:this.normalListeners[e],o=i?.get(t);return i&&o&&i.delete(t),0===i?.size&&(n?delete this.captureListeners[e]:delete this.normalListeners[e]),this.target.removeEventListener(e,o??t,normalizeOptions(s)),this}removeAllListeners(e){if(e){const t=this.normalListeners[e];t&&t.forEach((t,r)=>this.off(e,r));const r=this.captureListeners[e];r&&r.forEach((t,r)=>this.off(e,r,!0))}else Object.keys(this.normalListeners).forEach(e=>this.removeAllListeners(e)),Object.keys(this.captureListeners).forEach(e=>this.removeAllListeners(e));return this}removeAllListenersBut(...e){return 0===e.length?this.removeAllListeners():(Object.keys(this.normalListeners).forEach(t=>!e.includes(t)&&this.removeAllListeners(t)),Object.keys(this.captureListeners).forEach(t=>!e.includes(t)&&this.removeAllListeners(t)),this)}emit(e,...t){if(null==e)throw getEventTypeError();const[r,...s]=t;return this.getListenerList({event:e,wrapper:!0}).forEach(e=>{e(r,...s)}),this}}
1
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */
2
+ /* eslint-disable @typescript-eslint/no-dynamic-delete */
3
+ /* eslint-disable @typescript-eslint/strict-boolean-expressions */
4
+ import { isPassiveSupported, isDomEventTarget, isEventTargetLike, normalizeOptions, } from './EventEmitterListener.utils';
5
+ function getEventTypeError() {
6
+ return new Error('Event type can not be null.');
7
+ }
8
+ function getEventListenerError() {
9
+ return new Error('Event listener can not be null.');
10
+ }
11
+ export class EventEmitterListener {
12
+ target;
13
+ interceptor;
14
+ normalListeners = {};
15
+ captureListeners = {};
16
+ passiveSupported = isPassiveSupported();
17
+ constructor(target,
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ interceptor) {
20
+ this.target = target;
21
+ this.interceptor = interceptor;
22
+ this.target = target;
23
+ }
24
+ createWrapper(type, listener, once, ...rest) {
25
+ return (...params) => {
26
+ if (once) {
27
+ this.off(...[type, listener, ...rest]);
28
+ }
29
+ const nextParams = this.interceptor?.(...params) ?? params;
30
+ // if (typeof listener === 'object') {
31
+ // (listener.handleEvent as ListenerWrapper)(...params);
32
+ // } else {
33
+ listener(...nextParams);
34
+ // }
35
+ };
36
+ }
37
+ getListenerList({ event, type, wrapper } = {}) {
38
+ const map = (type === 'normal' && this.normalListeners) ||
39
+ (type === 'capture' && this.captureListeners) ||
40
+ undefined;
41
+ if (map) {
42
+ const entries = event ? map[event] && { [event]: map[event] } : map;
43
+ if (!entries)
44
+ return [];
45
+ return Object.values(entries).flatMap((m) => (m ? Array.from(wrapper ? m.values() : m.keys()) : []));
46
+ }
47
+ return this.getListenerList({ event, type: 'normal', wrapper }).concat(this.getListenerList({ event, type: 'capture', wrapper }));
48
+ }
49
+ getListeners({ event, type, wrapper } = {}) {
50
+ const map = (type === 'normal' && this.normalListeners) ||
51
+ (type === 'capture' && this.captureListeners) ||
52
+ undefined;
53
+ if (!map) {
54
+ const normal = this.getListeners({ event, type: 'normal', wrapper });
55
+ const capture = this.getListeners({ event, type: 'capture', wrapper });
56
+ const acc = {};
57
+ const callback = ([evtype, l]) => {
58
+ const list = acc[evtype];
59
+ acc[evtype] = list ? list.concat(l) : l;
60
+ };
61
+ Object.entries(normal).forEach(callback);
62
+ Object.entries(capture).forEach(callback);
63
+ return acc;
64
+ }
65
+ const entries = event ? map[event] && { [event]: map[event] } : map;
66
+ if (!entries)
67
+ return {};
68
+ return Object.entries(entries).reduce((acc, [evtype, m]) => {
69
+ const listeners = m ? Array.from(wrapper ? m.values() : m.keys()) : [];
70
+ if (listeners.length > 0)
71
+ acc[evtype] = listeners;
72
+ return acc;
73
+ }, {});
74
+ }
75
+ has(type, listener, ...rest) {
76
+ if (!isDomEventTarget(this.target)) {
77
+ // return !!this.normalListeners[type]?.has(listener);
78
+ const map = this.normalListeners[type];
79
+ return !!map && (listener ? map.has(listener) : map.size > 0);
80
+ }
81
+ // DOM
82
+ const options = rest[0];
83
+ const useCapture = options === true || (options && typeof options === 'object' && (options.capture ?? false));
84
+ const map = useCapture ? this.captureListeners[type] : this.normalListeners[type];
85
+ // return !!map?.has(listener);
86
+ return !!map && (listener ? map.has(listener) : map.size > 0);
87
+ }
88
+ on(type, listener, ...rest) {
89
+ if (type == null)
90
+ throw getEventTypeError();
91
+ if (listener == null)
92
+ throw getEventListenerError();
93
+ if (!isDomEventTarget(this.target)) {
94
+ const map = this.normalListeners[type] ?? new Map();
95
+ this.normalListeners[type] = map;
96
+ const handler = map.get(listener) ??
97
+ (this.interceptor ? this.createWrapper(type, listener, false, ...rest) : listener);
98
+ !map.has(listener) && map.set(listener, handler);
99
+ if (isEventTargetLike(this.target)) {
100
+ this.target.addEventListener(type, handler, ...rest);
101
+ }
102
+ else {
103
+ this.target.on(type, handler, ...rest);
104
+ }
105
+ return this;
106
+ }
107
+ // DOM
108
+ const options = rest[0];
109
+ const useCapture = options === true || (options && typeof options === 'object' && (options.capture ?? false));
110
+ const once = (options && typeof options === 'object' && options.once) ?? false;
111
+ if (useCapture) {
112
+ const map = this.captureListeners[type] ?? new Map();
113
+ this.captureListeners[type] = map;
114
+ const handler = map.get(listener) ?? this.createWrapper(type, listener, once, ...rest);
115
+ !map.has(listener) && map.set(listener, handler);
116
+ this.target.addEventListener(type, handler, normalizeOptions(options));
117
+ }
118
+ else {
119
+ const map = this.normalListeners[type] ?? new Map();
120
+ this.normalListeners[type] = map;
121
+ const handler = map.get(listener) ?? this.createWrapper(type, listener, once, ...rest);
122
+ !map.has(listener) && map.set(listener, handler);
123
+ this.target.addEventListener(type, handler, normalizeOptions(options));
124
+ }
125
+ return this;
126
+ }
127
+ once(type, listener, ...rest) {
128
+ if (type == null)
129
+ throw getEventTypeError();
130
+ if (listener == null)
131
+ throw getEventListenerError();
132
+ if (!isDomEventTarget(this.target)) {
133
+ const map = this.normalListeners[type] ?? new Map();
134
+ this.normalListeners[type] = map;
135
+ const handler = map.get(listener) ?? this.createWrapper(type, listener, true, ...rest);
136
+ !map.has(listener) && map.set(listener, handler);
137
+ if (isEventTargetLike(this.target)) {
138
+ this.target.addEventListener(type, handler, ...rest);
139
+ }
140
+ else if (this.target.once) {
141
+ this.target.once(type, handler, ...rest);
142
+ }
143
+ else {
144
+ this.target.on(type, handler, ...rest);
145
+ }
146
+ return this;
147
+ }
148
+ // DOM
149
+ const options = rest[0];
150
+ return this.on(...[
151
+ type,
152
+ listener,
153
+ {
154
+ ...(typeof options === 'object' ? options : options != null && { capture: options }),
155
+ once: true,
156
+ },
157
+ ]);
158
+ }
159
+ off(type, listener, ...rest) {
160
+ if (!isDomEventTarget(this.target)) {
161
+ const map = this.normalListeners[type];
162
+ const wrapper = map?.get(listener);
163
+ map && wrapper && map.delete(listener);
164
+ if (map?.size === 0) {
165
+ delete this.normalListeners[type];
166
+ }
167
+ if (isEventTargetLike(this.target)) {
168
+ this.target.removeEventListener(type, wrapper ?? listener, ...rest);
169
+ }
170
+ else {
171
+ this.target.off(type, wrapper ?? listener, ...rest);
172
+ }
173
+ return this;
174
+ }
175
+ // DOM
176
+ const options = rest[0];
177
+ const useCapture = options === true || (options && typeof options === 'object' && (options.capture ?? false));
178
+ const map = useCapture ? this.captureListeners[type] : this.normalListeners[type];
179
+ const wrapper = map?.get(listener);
180
+ map && wrapper && map.delete(listener);
181
+ if (map?.size === 0) {
182
+ if (useCapture)
183
+ delete this.captureListeners[type];
184
+ else
185
+ delete this.normalListeners[type];
186
+ }
187
+ this.target.removeEventListener(type, wrapper ?? listener, normalizeOptions(options));
188
+ return this;
189
+ }
190
+ removeAllListeners(type) {
191
+ if (type) {
192
+ const normalMap = this.normalListeners[type];
193
+ normalMap?.forEach((_, listener) => this.off(...[type, listener]));
194
+ const captureMap = this.captureListeners[type];
195
+ captureMap?.forEach((_, listener) => this.off(...[type, listener, true]));
196
+ }
197
+ else {
198
+ Object.keys(this.normalListeners).forEach((k) => this.removeAllListeners(k));
199
+ Object.keys(this.captureListeners).forEach((k) => this.removeAllListeners(k));
200
+ }
201
+ return this;
202
+ }
203
+ removeAllListenersBut(...types) {
204
+ if (types.length === 0)
205
+ return this.removeAllListeners();
206
+ Object.keys(this.normalListeners).forEach((k) => !types.includes(k) && this.removeAllListeners(k));
207
+ Object.keys(this.captureListeners).forEach((k) => !types.includes(k) && this.removeAllListeners(k));
208
+ return this;
209
+ }
210
+ emit(type, ...args) {
211
+ if (type == null)
212
+ throw getEventTypeError();
213
+ const [event, ...rest] = args;
214
+ this.getListenerList({ event: type, wrapper: true }).forEach((l) => {
215
+ l(event, ...rest);
216
+ });
217
+ return this;
218
+ }
219
+ }
220
+ // new EventEmitterListener({} as HTMLVideoElement).on('encrypted0', (e) => e, {});
221
+ // new EventEmitterListener({} as HTMLVideoElement).removeAllListeners('sdff');
222
+ // new EventEmitterListener({} as EventEmitterTarget).on('encrypted', (e) => e, {});
@@ -1,32 +1,43 @@
1
1
  import type { EventEmitter } from '@js-toolkit/utils/EventEmitter';
2
2
  export type DomEventTarget = EventTarget;
3
- export type EventTargetLike = {
3
+ export interface EventTargetLike {
4
4
  addEventListener: EventEmitterLike['on'];
5
5
  removeEventListener: EventEmitterLike['off'];
6
- };
7
- export type EventEmitterLike = {
6
+ }
7
+ export interface EventEmitterLike {
8
8
  on: (type: any, listener: AnyFunction, ...rest: any[]) => void;
9
9
  once?: ((type: any, listener: AnyFunction, ...rest: any[]) => void) | undefined;
10
10
  off: (type: any, listener: AnyFunction, ...rest: any[]) => void;
11
- };
11
+ }
12
12
  export type EmitterTarget = DomEventTarget | EventTargetLike | EventEmitterLike;
13
13
  export type GetDomEventType<T extends DomEventTarget> = T['addEventListener'] extends {
14
14
  (type: infer K, listener: (this: T, ev: any) => any, options?: boolean | AddEventListenerOptions): void;
15
15
  (type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
16
16
  } ? K : string;
17
- export type GetDomEventListener<E, EM extends AnyObject> = (ev: E extends keyof EM ? EM[E] : Event, ...rest: unknown[]) => unknown;
18
- export type GetEventTypeFromFn<T extends EventEmitterLike['on']> = T extends {
19
- (type: infer K, listener: AnyFunction, ...rest: unknown[]): unknown;
20
- } ? K : string;
21
- export type GetEventType<T extends EmitterTarget> = T extends DomEventTarget ? GetDomEventType<T> : T extends EventEmitterLike ? GetEventTypeFromFn<T['on']> : T extends EventTargetLike ? GetEventTypeFromFn<T['addEventListener']> : string;
22
- export type GetEventListener<T extends EmitterTarget, E, EM extends AnyObject = GetEventMap<T>> = T extends DomEventTarget ? IfExtends<EM, EmptyObject, EventListener, GetDomEventListener<E, EM>> : IfExtends<EM, EmptyObject, Parameters<T extends EventEmitterLike ? T['on'] : T extends EventTargetLike ? T['addEventListener'] : AnyFunction>['1'], (event: E extends keyof EM ? EM[E] : unknown, ...rest: any[]) => unknown>;
17
+ export type GetDomEventListener<E, EM extends AnyObject> = (ev: E extends keyof EM ? EM[E] : Event, ...rest: any[]) => any;
18
+ /** https://github.com/microsoft/TypeScript/issues/32164 */
19
+ export type GetEventTypeFromFn<T> = T extends {
20
+ (type: infer K1, listener: AnyFunction, ...rest: any[]): any;
21
+ (type: infer K2, listener: AnyFunction, ...rest: any[]): any;
22
+ (type: infer K3, listener: AnyFunction, ...rest: any[]): any;
23
+ (type: infer K4, listener: AnyFunction, ...rest: any[]): any;
24
+ } ? K1 | K2 | K3 | K4 : T extends {
25
+ (type: infer K1, listener: AnyFunction, ...rest: any[]): any;
26
+ (type: infer K2, listener: AnyFunction, ...rest: any[]): any;
27
+ (type: infer K3, listener: AnyFunction, ...rest: any[]): any;
28
+ } ? K1 | K2 | K3 : T extends {
29
+ (type: infer K1, listener: AnyFunction, ...rest: any[]): any;
30
+ (type: infer K2, listener: AnyFunction, ...rest: any[]): any;
31
+ } ? K1 | K2 : T extends (type: infer K, listener: AnyFunction, ...rest: any[]) => any ? K : string;
32
+ export type GetEventType<T> = T extends DomEventTarget ? GetDomEventType<T> : T extends EventEmitterLike ? GetEventTypeFromFn<T['on']> : T extends EventTargetLike ? GetEventTypeFromFn<T['addEventListener']> : string;
33
+ export type GetEventListener<T, E, EM extends AnyObject = GetEventMap<T>> = T extends DomEventTarget ? IfExtends<EM, EmptyObject, EventListener, GetDomEventListener<E, EM>> : IfExtends<EM, EmptyObject, Parameters<T extends EventEmitterLike ? T['on'] : T extends EventTargetLike ? T['addEventListener'] : AnyFunction>['1'], (event: E extends keyof EM ? EM[E] : unknown, ...rest: any[]) => unknown>;
23
34
  type ListenersMapToEventMap<T extends Record<string, AnyFunction[]>> = {
24
35
  [P in keyof T]: Parameters<T[P][number]>[0];
25
36
  };
26
37
  export type GetEventMap<T> = T extends Window ? WindowEventMap : T extends ServiceWorker ? ServiceWorkerEventMap : T extends Worker ? WorkerEventMap : T extends ShadowRoot ? ShadowRootEventMap : T extends Document ? DocumentEventMap : T extends HTMLBodyElement ? HTMLBodyElementEventMap : T extends HTMLVideoElement ? HTMLVideoElementEventMap : T extends HTMLMediaElement ? HTMLMediaElementEventMap : T extends TextTrackList ? TextTrackListEventMap : T extends HTMLElement ? HTMLElementEventMap : T extends Element ? ElementEventMap : T extends TextTrackList ? TextTrackListEventMap : T extends TextTrackCue ? TextTrackCueEventMap : T extends Animation ? AnimationEventMap : T extends AbortSignal ? AbortSignalEventMap : T extends BroadcastChannel ? BroadcastChannelEventMap : T extends WebSocket ? WebSocketEventMap : T extends MediaStreamTrack ? MediaStreamTrackEventMap : T extends MediaStream ? MediaStreamEventMap : T extends EventSource ? EventSourceEventMap : T extends SourceBuffer ? SourceBufferEventMap : T extends SourceBufferList ? SourceBufferListEventMap : T extends RemotePlayback ? RemotePlaybackEventMap : T extends EventEmitter<any, any> ? ListenersMapToEventMap<ReturnType<T['getEventListeners']>> : EmptyObject;
27
- export declare function isEventTargetLike(target: EmitterTarget): target is EventTargetLike;
28
- export declare function isDomEventTarget(target: EmitterTarget): target is DomEventTarget;
29
- export declare function isEventEmitterLike(target: EmitterTarget): target is EventEmitterLike;
38
+ export declare function isEventTargetLike(target: unknown): target is EventTargetLike;
39
+ export declare function isDomEventTarget(target: unknown): target is DomEventTarget;
40
+ export declare function isEventEmitterLike(target: unknown): target is EventEmitterLike;
30
41
  export declare function isPassiveSupported(): boolean;
31
42
  export declare function normalizeOptions(options: boolean | AddEventListenerOptions | undefined): typeof options;
32
43
  export {};
@@ -1 +1,41 @@
1
- import{isEmptyObject}from"@js-toolkit/utils/isEmptyObject";export function isEventTargetLike(e){return void 0!==e.addEventListener&&void 0!==e.removeEventListener}export function isDomEventTarget(e){return isEventTargetLike(e)&&void 0!==e.dispatchEvent}export function isEventEmitterLike(e){return!isEventTargetLike(e)&&!isDomEventTarget(e)&&void 0!==e.on&&void 0!==e.once&&void 0!==e.off}let passiveSupported=!1;export function isPassiveSupported(){return passiveSupported}try{const e={get passive(){return passiveSupported=!0,!1}};window.addEventListener("__testpassive__",null,e)}catch{}export function normalizeOptions(e){if(e&&"object"==typeof e){let t=e;if("passive"in e&&!passiveSupported){const{passive:i,...n}=e;t=n}return isEmptyObject(t)?void 0:t}return e}
1
+ import { isEmptyObject } from '@js-toolkit/utils/isEmptyObject';
2
+ export function isEventTargetLike(target) {
3
+ return (target.addEventListener !== undefined &&
4
+ target.removeEventListener !== undefined);
5
+ }
6
+ export function isDomEventTarget(target) {
7
+ return isEventTargetLike(target) && target.dispatchEvent !== undefined;
8
+ }
9
+ export function isEventEmitterLike(target) {
10
+ return (!isEventTargetLike(target) &&
11
+ !isDomEventTarget(target) &&
12
+ target.on !== undefined &&
13
+ target.once !== undefined &&
14
+ target.off !== undefined);
15
+ }
16
+ let passiveSupported = false;
17
+ export function isPassiveSupported() {
18
+ return passiveSupported;
19
+ }
20
+ try {
21
+ const options = {
22
+ get passive() {
23
+ passiveSupported = true;
24
+ return false;
25
+ },
26
+ };
27
+ window.addEventListener('__testpassive__', null, options);
28
+ }
29
+ catch { }
30
+ export function normalizeOptions(options) {
31
+ if (options != null && typeof options === 'object') {
32
+ let result = options;
33
+ if ('passive' in options && !passiveSupported) {
34
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
35
+ const { passive, ...rest } = options;
36
+ result = rest;
37
+ }
38
+ return isEmptyObject(result) ? undefined : result;
39
+ }
40
+ return options;
41
+ }
package/EventListeners.js CHANGED
@@ -1 +1,58 @@
1
- import{EventEmitterListener}from"./EventEmitterListener";export class EventListeners{listeners=new Map;getScopes(){return this.listeners.keys()}scope(e,s){const t=s??"",r=this.listeners.get(e)??new Map;!this.listeners.has(e)&&this.listeners.set(e,r);const i=r.get(t)??new EventEmitterListener(e);return!r.has(t)&&r.set(t,i),i}removeAllListeners(e,s){if(e){const t=this.listeners.get(e);s?(t?.get(s)?.removeAllListeners(),t?.delete(s),0===t?.size&&this.listeners.delete(e)):(t?.forEach(e=>e.removeAllListeners()),t?.clear(),this.listeners.delete(e))}else this.listeners.forEach(e=>{e.forEach(e=>e.removeAllListeners()),e.clear()}),this.listeners.clear();return this}}
1
+ /* eslint-disable @typescript-eslint/no-unnecessary-type-parameters */
2
+ import { EventEmitterListener } from './EventEmitterListener';
3
+ export class EventListeners {
4
+ // private readonly listeners = new Map<EmitterTarget, EventEmitterListener<any, any>>();
5
+ // scope<T extends EmitterTarget, M extends AnyObject = GetEventMap<T>>(
6
+ // target: T
7
+ // ): EventEmitterListener<T, M> {
8
+ // const listener = this.listeners.get(target) ?? new EventEmitterListener<T, M>(target);
9
+ // !this.listeners.has(target) && this.listeners.set(target, listener);
10
+ // return listener as EventEmitterListener<T, M>;
11
+ // }
12
+ // removeAllListeners<T extends EmitterTarget>(target?: T | undefined): this {
13
+ // if (target) {
14
+ // this.listeners.get(target)?.removeAllListeners();
15
+ // this.listeners.delete(target);
16
+ // } else {
17
+ // this.listeners.forEach((l) => l.removeAllListeners());
18
+ // this.listeners.clear();
19
+ // }
20
+ // return this;
21
+ // }
22
+ listeners = new Map();
23
+ getScopes() {
24
+ return this.listeners.keys();
25
+ }
26
+ scope(target, scope) {
27
+ const id = scope ?? '';
28
+ const scopeMap = this.listeners.get(target) ??
29
+ new Map();
30
+ !this.listeners.has(target) && this.listeners.set(target, scopeMap);
31
+ const listener = scopeMap.get(id) ?? new EventEmitterListener(target);
32
+ !scopeMap.has(id) && scopeMap.set(id, listener);
33
+ return listener;
34
+ }
35
+ removeAllListeners(target, scope) {
36
+ if (target) {
37
+ const map = this.listeners.get(target);
38
+ if (scope) {
39
+ map?.get(scope)?.removeAllListeners();
40
+ map?.delete(scope);
41
+ map?.size === 0 && this.listeners.delete(target);
42
+ }
43
+ else {
44
+ map?.forEach((l) => l.removeAllListeners());
45
+ map?.clear();
46
+ this.listeners.delete(target);
47
+ }
48
+ }
49
+ else {
50
+ this.listeners.forEach((m) => {
51
+ m.forEach((l) => l.removeAllListeners());
52
+ m.clear();
53
+ });
54
+ this.listeners.clear();
55
+ }
56
+ return this;
57
+ }
58
+ }
@@ -1 +1,193 @@
1
- import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{hasIn}from"@js-toolkit/utils/hasIn";import{toggleNativeSubtitles}from"./media/toggleNativeSubtitles";import{fullscreen}from"./fullscreen";import{enterPseudoFullscreen}from"./fullscreenUtils";export class FullscreenController extends EventEmitter{element;options;static isApiAvailable(){return fullscreen.isApiEnabled()}get Events(){return FullscreenController.Events}fallback;exitPseudoFullscreen;constructor(e,t={}){super(),this.element=e,this.options=t,this.setOptions(t)}unbind(){if(fullscreen.names){const{names:e}=fullscreen;this.element.removeEventListener(e.changeEventName,this.nativeChangeHandler),this.element.removeEventListener(e.errorEventName,this.nativeErrorHandler)}this.fallback instanceof HTMLVideoElement&&(this.fallback.removeEventListener("webkitbeginfullscreen",this.videoBeginFullscreenHandler),this.fallback.removeEventListener("webkitendfullscreen",this.videoEndFullscreenHandler),this.fallback=void 0)}setOptions(e){if(this.unbind(),this.options=e??{},this.fallback=this.options.fallback,fullscreen.names&&fullscreen.isApiEnabled()){const{names:e}=fullscreen;this.element.addEventListener(e.changeEventName,this.nativeChangeHandler),this.element.addEventListener(e.errorEventName,this.nativeErrorHandler)}else this.fallback instanceof HTMLVideoElement&&(this.fallback.addEventListener("webkitbeginfullscreen",this.videoBeginFullscreenHandler),this.fallback.addEventListener("webkitendfullscreen",this.videoEndFullscreenHandler))}isAvailable(){return fullscreen.isApiEnabled()||"pseudo"===this.fallback||this.fallback instanceof HTMLVideoElement&&!!this.fallback.webkitEnterFullscreen&&!!this.fallback.webkitSupportsFullscreen}isFullscreen(){return!!this.getCurrentElement()}isPseudoFullscreen(){return this.isFullscreen()&&!!this.exitPseudoFullscreen}getCurrentElement(){if(fullscreen.isApiEnabled()){if(fullscreen.getElement()===this.element)return this.element}else{if(this.exitPseudoFullscreen)return this.element;if(this.fallback instanceof HTMLVideoElement&&this.fallback.webkitDisplayingFullscreen)return this.fallback}return null}nativeChangeHandler=()=>{this.emit(this.Events.Change,{fullscreen:this.isFullscreen(),type:"native"})};nativeErrorHandler=e=>{this.emit(this.Events.Error,{error:e,type:"native"})};videoBeginFullscreenHandler=(()=>{const e=()=>{const t=this.fallback;e.controls=t.controls,e.nativeSubtitles=hasIn(this.options,"toggleNativeSubtitles")&&this.options.toggleNativeSubtitles&&t.textTracks.length>0,e.nativeSubtitles&&toggleNativeSubtitles(!0,t.textTracks),this.emit(this.Events.Change,{fullscreen:!0,type:"video"})};return e.nativeSubtitles=void 0,e.controls=void 0,e})();videoEndFullscreenHandler=()=>{const e=this.fallback;null!=this.videoBeginFullscreenHandler.controls&&(e.controls=this.videoBeginFullscreenHandler.controls),this.videoBeginFullscreenHandler.nativeSubtitles&&toggleNativeSubtitles(!1,e.textTracks),this.emit(this.Events.Change,{fullscreen:!1,type:"video"})};request(e={}){return new Promise((t,n)=>{if(this.isFullscreen())return void t();if(fullscreen.isApiEnabled())return void fullscreen.request(this.element,e).then(t).catch(n);if("pseudo"===this.fallback)return this.exitPseudoFullscreen=enterPseudoFullscreen(this.element),this.emit(this.Events.Change,{fullscreen:!0,type:"pseudo"}),void t();const l=this.fallback instanceof HTMLVideoElement&&this.fallback||void 0;if(l?.webkitEnterFullscreen&&l.webkitSupportsFullscreen){const e=()=>{l.removeEventListener("webkitbeginfullscreen",e),t()};return l.addEventListener("webkitbeginfullscreen",e),void l.webkitEnterFullscreen()}n(new fullscreen.UnavailableError)})}exit(){return new Promise((e,t)=>{if(!this.isFullscreen())return void e();if(fullscreen.isApiEnabled())return void fullscreen.exit().then(e).catch(t);if(this.exitPseudoFullscreen)return this.exitPseudoFullscreen(),this.exitPseudoFullscreen=void 0,this.emit(this.Events.Change,{fullscreen:!1,type:"pseudo"}),void e();const n=this.fallback instanceof HTMLVideoElement&&this.fallback||void 0;if(n?.webkitExitFullscreen&&n.webkitSupportsFullscreen){const t=()=>{n.removeEventListener("webkitendfullscreen",t),e()};return n.addEventListener("webkitendfullscreen",t),void n.webkitExitFullscreen()}t(new fullscreen.UnavailableError)})}destroy(){return this.exit().then(()=>new Promise(e=>{requestAnimationFrame(()=>{this.removeAllListeners(),this.unbind(),e()})}))}[Symbol.asyncDispose](){return this.destroy()}}!function(e){let t;!function(e){e.Change="change",e.Error="error"}(t=e.Events||(e.Events={}))}(FullscreenController||(FullscreenController={}));
1
+ import { EventEmitter } from '@js-toolkit/utils/EventEmitter';
2
+ import { hasIn } from '@js-toolkit/utils/hasIn';
3
+ import { toggleNativeSubtitles } from './media/toggleNativeSubtitles';
4
+ import { fullscreen } from './fullscreen';
5
+ import { enterPseudoFullscreen } from './fullscreenUtils';
6
+ export class FullscreenController extends EventEmitter {
7
+ element;
8
+ options;
9
+ static isApiAvailable() {
10
+ return fullscreen.isApiEnabled();
11
+ }
12
+ // eslint-disable-next-line class-methods-use-this
13
+ get Events() {
14
+ return FullscreenController.Events;
15
+ }
16
+ fallback;
17
+ exitPseudoFullscreen;
18
+ constructor(element, options = {}) {
19
+ super();
20
+ this.element = element;
21
+ this.options = options;
22
+ this.setOptions(options);
23
+ }
24
+ unbind() {
25
+ if (fullscreen.names) {
26
+ const { names } = fullscreen;
27
+ this.element.removeEventListener(names.changeEventName, this.nativeChangeHandler);
28
+ this.element.removeEventListener(names.errorEventName, this.nativeErrorHandler);
29
+ }
30
+ if (this.fallback instanceof HTMLVideoElement) {
31
+ this.fallback.removeEventListener('webkitbeginfullscreen', this.videoBeginFullscreenHandler);
32
+ this.fallback.removeEventListener('webkitendfullscreen', this.videoEndFullscreenHandler);
33
+ this.fallback = undefined;
34
+ }
35
+ }
36
+ setOptions(options) {
37
+ this.unbind();
38
+ this.options = options ?? {};
39
+ this.fallback = this.options.fallback;
40
+ if (fullscreen.names && fullscreen.isApiEnabled()) {
41
+ const { names } = fullscreen;
42
+ this.element.addEventListener(names.changeEventName, this.nativeChangeHandler);
43
+ this.element.addEventListener(names.errorEventName, this.nativeErrorHandler);
44
+ }
45
+ // Use video if regular fullscreen api unavailable (probably ios).
46
+ else if (this.fallback instanceof HTMLVideoElement) {
47
+ this.fallback.addEventListener('webkitbeginfullscreen', this.videoBeginFullscreenHandler);
48
+ this.fallback.addEventListener('webkitendfullscreen', this.videoEndFullscreenHandler);
49
+ }
50
+ }
51
+ isAvailable() {
52
+ return (fullscreen.isApiEnabled() ||
53
+ this.fallback === 'pseudo' ||
54
+ (this.fallback instanceof HTMLVideoElement &&
55
+ !!this.fallback.webkitEnterFullscreen &&
56
+ !!this.fallback.webkitSupportsFullscreen));
57
+ }
58
+ isFullscreen() {
59
+ return !!this.getCurrentElement();
60
+ }
61
+ isPseudoFullscreen() {
62
+ return this.isFullscreen() && !!this.exitPseudoFullscreen;
63
+ }
64
+ getCurrentElement() {
65
+ if (fullscreen.isApiEnabled()) {
66
+ if (fullscreen.getElement() === this.element)
67
+ return this.element;
68
+ }
69
+ else if (this.exitPseudoFullscreen) {
70
+ return this.element;
71
+ }
72
+ else if (this.fallback instanceof HTMLVideoElement &&
73
+ this.fallback.webkitDisplayingFullscreen) {
74
+ return this.fallback;
75
+ }
76
+ return null;
77
+ }
78
+ nativeChangeHandler = () => {
79
+ this.emit(this.Events.Change, { fullscreen: this.isFullscreen(), type: 'native' });
80
+ };
81
+ nativeErrorHandler = (event) => {
82
+ this.emit(this.Events.Error, { error: event, type: 'native' });
83
+ };
84
+ videoBeginFullscreenHandler = (() => {
85
+ const handler = () => {
86
+ const video = this.fallback;
87
+ handler.controls = video.controls;
88
+ handler.nativeSubtitles =
89
+ hasIn(this.options, 'toggleNativeSubtitles') &&
90
+ this.options.toggleNativeSubtitles &&
91
+ video.textTracks.length > 0;
92
+ if (handler.nativeSubtitles)
93
+ toggleNativeSubtitles(true, video.textTracks);
94
+ this.emit(this.Events.Change, { fullscreen: true, type: 'video' });
95
+ };
96
+ handler.nativeSubtitles = undefined;
97
+ handler.controls = undefined;
98
+ return handler;
99
+ })();
100
+ videoEndFullscreenHandler = () => {
101
+ const video = this.fallback;
102
+ // Fix swiped exit fullscreen
103
+ if (this.videoBeginFullscreenHandler.controls != null) {
104
+ video.controls = this.videoBeginFullscreenHandler.controls;
105
+ }
106
+ if (this.videoBeginFullscreenHandler.nativeSubtitles) {
107
+ toggleNativeSubtitles(false, video.textTracks);
108
+ }
109
+ this.emit(this.Events.Change, { fullscreen: false, type: 'video' });
110
+ };
111
+ request(options = {}) {
112
+ return new Promise((resolve, reject) => {
113
+ if (this.isFullscreen()) {
114
+ resolve();
115
+ return;
116
+ }
117
+ if (fullscreen.isApiEnabled()) {
118
+ fullscreen.request(this.element, options).then(resolve).catch(reject);
119
+ return;
120
+ }
121
+ if (this.fallback === 'pseudo') {
122
+ this.exitPseudoFullscreen = enterPseudoFullscreen(this.element);
123
+ this.emit(this.Events.Change, { fullscreen: true, type: 'pseudo' });
124
+ resolve();
125
+ return;
126
+ }
127
+ const video = this.fallback instanceof HTMLVideoElement ? this.fallback : undefined;
128
+ if (video?.webkitEnterFullscreen && video.webkitSupportsFullscreen) {
129
+ const beginFullscreenHandler = () => {
130
+ video.removeEventListener('webkitbeginfullscreen', beginFullscreenHandler);
131
+ resolve();
132
+ };
133
+ video.addEventListener('webkitbeginfullscreen', beginFullscreenHandler);
134
+ video.webkitEnterFullscreen();
135
+ return;
136
+ }
137
+ reject(new fullscreen.UnavailableError());
138
+ });
139
+ }
140
+ exit() {
141
+ return new Promise((resolve, reject) => {
142
+ if (!this.isFullscreen()) {
143
+ resolve();
144
+ return;
145
+ }
146
+ if (fullscreen.isApiEnabled()) {
147
+ fullscreen.exit().then(resolve).catch(reject);
148
+ return;
149
+ }
150
+ if (this.exitPseudoFullscreen) {
151
+ this.exitPseudoFullscreen();
152
+ this.exitPseudoFullscreen = undefined;
153
+ this.emit(this.Events.Change, { fullscreen: false, type: 'pseudo' });
154
+ resolve();
155
+ return;
156
+ }
157
+ const video = this.fallback instanceof HTMLVideoElement ? this.fallback : undefined;
158
+ if (video?.webkitExitFullscreen && video.webkitSupportsFullscreen) {
159
+ const endFullscreenHandler = () => {
160
+ video.removeEventListener('webkitendfullscreen', endFullscreenHandler);
161
+ resolve();
162
+ };
163
+ video.addEventListener('webkitendfullscreen', endFullscreenHandler);
164
+ video.webkitExitFullscreen();
165
+ return;
166
+ }
167
+ reject(new fullscreen.UnavailableError());
168
+ });
169
+ }
170
+ destroy() {
171
+ return this.exit().then(() => {
172
+ // Because of unbind is calling before the change event will be fired.
173
+ return new Promise((resolve) => {
174
+ requestAnimationFrame(() => {
175
+ this.removeAllListeners();
176
+ this.unbind();
177
+ resolve();
178
+ });
179
+ });
180
+ });
181
+ }
182
+ [Symbol.asyncDispose]() {
183
+ return this.destroy();
184
+ }
185
+ }
186
+ // eslint-disable-next-line @typescript-eslint/no-namespace
187
+ (function (FullscreenController) {
188
+ let Events;
189
+ (function (Events) {
190
+ Events["Change"] = "change";
191
+ Events["Error"] = "error";
192
+ })(Events = FullscreenController.Events || (FullscreenController.Events = {}));
193
+ })(FullscreenController || (FullscreenController = {}));