@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.
- package/EventEmitterListener.d.ts +3 -3
- package/EventEmitterListener.js +222 -1
- package/EventEmitterListener.utils.d.ts +24 -13
- package/EventEmitterListener.utils.js +41 -1
- package/EventListeners.js +58 -1
- package/FullscreenController.js +193 -1
- package/README.md +159 -20
- package/WakeLockController.js +76 -1
- package/base64ToDataUrl.js +3 -1
- package/blobToDataUrl.js +10 -1
- package/checkPermission.js +8 -1
- package/copyToClipboard.js +37 -1
- package/createLoop.js +30 -1
- package/createRafLoop.js +56 -1
- package/dataUrlToBlob.js +13 -1
- package/fromBase64.js +10 -1
- package/fullscreen.js +167 -1
- package/fullscreenUtils.js +37 -1
- package/getAspectRatio.js +8 -1
- package/getBrowserLanguage.js +10 -1
- package/getCurrentScriptUrl.js +4 -1
- package/getEventAwaiter.js +41 -1
- package/getGeoCoordinates.js +6 -1
- package/getGeoLocality.js +19 -1
- package/getInnerRect.js +8 -1
- package/getInnerXDimensions.js +9 -1
- package/getInnerYDimensions.js +9 -1
- package/getPinchZoomHandlers.js +134 -1
- package/getRandomID.js +4 -1
- package/getScreenSize.js +23 -1
- package/getSecondsCounter.js +47 -1
- package/iframe/getAutoConnector.js +251 -1
- package/iframe/getOriginFromMessage.js +3 -1
- package/iframe/isIframeLoaded.js +9 -1
- package/iframe/messages.d.ts +2 -2
- package/iframe/messages.js +50 -1
- package/iframe/utils.js +33 -1
- package/imageToBlob.js +20 -1
- package/isImageTypeSupported.js +8 -1
- package/isWebPSupported.js +15 -1
- package/loadImage.js +29 -1
- package/loadScript.d.ts +1 -1
- package/loadScript.js +67 -1
- package/media/Capabilities.js +44 -1
- package/media/MediaNotAttachedError.d.ts +1 -1
- package/media/MediaNotAttachedError.js +6 -1
- package/media/MediaStreamController.js +84 -1
- package/media/PipController.d.ts +2 -2
- package/media/PipController.js +140 -1
- package/media/TextTracksController/TextTracksController.d.ts +0 -3
- package/media/TextTracksController/TextTracksController.js +251 -1
- package/media/TextTracksController/index.js +1 -1
- package/media/TextTracksController/utils.js +147 -1
- package/media/getDurationTime.js +3 -1
- package/media/getMediaSource.js +11 -1
- package/media/getSourceBuffer.js +5 -1
- package/media/isMediaSeekable.js +4 -1
- package/media/parseCueText.js +224 -1
- package/media/resetMedia.js +17 -1
- package/media/timeRanges.js +9 -1
- package/media/toggleNativeSubtitles.js +21 -1
- package/metrics/ga/DataLayerProxy.js +11 -1
- package/metrics/ga/getHandler.js +99 -1
- package/metrics/ga/types.js +1 -1
- package/metrics/types.js +1 -1
- package/metrics/yandex/DataLayerProxy.js +11 -1
- package/metrics/yandex/getHandler.js +63 -1
- package/metrics/yandex/types.js +2 -1
- package/onDOMReady.js +12 -1
- package/onPageReady.js +27 -1
- package/package.json +19 -16
- package/patchConsoleLogging.js +27 -1
- package/performance/getNavigationTiming.js +14 -1
- package/platform/Semver.js +23 -1
- package/platform/getChromeVersion.d.ts +2 -0
- package/platform/getChromeVersion.js +16 -0
- package/platform/getIOSVersion.js +18 -1
- package/platform/getPlatformInfo.js +49 -1
- package/platform/isAirPlayAvailable.js +3 -1
- package/platform/isAndroid.js +7 -1
- package/platform/isChrome.js +7 -1
- package/platform/isEMESupported.js +9 -1
- package/platform/isIOS.js +9 -1
- package/platform/isMSESupported.js +16 -1
- package/platform/isMacOS.js +12 -1
- package/platform/isMediaCapabilitiesSupported.js +6 -1
- package/platform/isMobile.js +16 -1
- package/platform/isMobileSimulation.js +7 -1
- package/platform/isSafari.js +14 -1
- package/platform/isScreenHDR.js +4 -1
- package/platform/isStandaloneApp.js +4 -1
- package/platform/isTelegramWebView.js +3 -1
- package/platform/isTouchSupported.js +4 -1
- package/preventDefault.d.ts +2 -2
- package/preventDefault.js +5 -1
- package/rafCallback.js +9 -1
- package/responsive/MediaQuery.d.ts +18 -0
- package/responsive/MediaQuery.js +40 -0
- package/responsive/MediaQueryListener.d.ts +19 -0
- package/responsive/MediaQueryListener.js +55 -0
- package/responsive/ViewSize.d.ts +45 -0
- package/responsive/ViewSize.js +82 -0
- package/responsive/getViewSizeQueryMap.d.ts +2 -0
- package/responsive/getViewSizeQueryMap.js +7 -0
- package/saveFileAs.js +22 -1
- package/serviceWorker/ServiceWorkerInstaller.d.ts +0 -1
- package/serviceWorker/ServiceWorkerInstaller.js +112 -1
- package/serviceWorker/utils.d.ts +1 -1
- package/serviceWorker/utils.js +86 -1
- package/stopPropagation.d.ts +2 -2
- package/stopPropagation.js +3 -1
- package/takeScreenshot.js +51 -1
- package/toBase64.js +9 -1
- package/toLocalPoint.js +9 -1
- package/types/index.js +2 -1
- package/types/refs.js +1 -1
- package/viewableTracker.js +69 -1
- package/webrtc/PeerConnection.js +212 -1
- package/webrtc/sdputils.js +417 -1
- 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
|
|
3
|
-
type GetOnceOptions<T
|
|
4
|
-
type GetOffOptions<T
|
|
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;
|
package/EventEmitterListener.js
CHANGED
|
@@ -1 +1,222 @@
|
|
|
1
|
-
|
|
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
|
|
3
|
+
export interface EventTargetLike {
|
|
4
4
|
addEventListener: EventEmitterLike['on'];
|
|
5
5
|
removeEventListener: EventEmitterLike['off'];
|
|
6
|
-
}
|
|
7
|
-
export
|
|
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:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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:
|
|
28
|
-
export declare function isDomEventTarget(target:
|
|
29
|
-
export declare function isEventEmitterLike(target:
|
|
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
|
|
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
|
-
|
|
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
|
+
}
|
package/FullscreenController.js
CHANGED
|
@@ -1 +1,193 @@
|
|
|
1
|
-
import{EventEmitter}from
|
|
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 = {}));
|