@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
package/fullscreenUtils.js
CHANGED
|
@@ -1 +1,37 @@
|
|
|
1
|
-
export function enterPseudoFullscreen(
|
|
1
|
+
export function enterPseudoFullscreen(element) {
|
|
2
|
+
let originStyle;
|
|
3
|
+
let currentEl;
|
|
4
|
+
originStyle = {
|
|
5
|
+
position: element.style.position,
|
|
6
|
+
left: element.style.left,
|
|
7
|
+
top: element.style.top,
|
|
8
|
+
width: element.style.width,
|
|
9
|
+
height: element.style.height,
|
|
10
|
+
maxWidth: element.style.maxWidth,
|
|
11
|
+
maxHeight: element.style.maxHeight,
|
|
12
|
+
zIndex: element.style.zIndex,
|
|
13
|
+
};
|
|
14
|
+
currentEl = element;
|
|
15
|
+
currentEl.style.position = 'fixed';
|
|
16
|
+
currentEl.style.left = '0px';
|
|
17
|
+
currentEl.style.top = '0px';
|
|
18
|
+
currentEl.style.width = '100%';
|
|
19
|
+
currentEl.style.height = '100%';
|
|
20
|
+
currentEl.style.maxWidth = '100%';
|
|
21
|
+
currentEl.style.maxHeight = '100%';
|
|
22
|
+
currentEl.style.zIndex = '99999';
|
|
23
|
+
return () => {
|
|
24
|
+
if (originStyle && currentEl) {
|
|
25
|
+
currentEl.style.position = originStyle.position;
|
|
26
|
+
currentEl.style.left = originStyle.left;
|
|
27
|
+
currentEl.style.top = originStyle.top;
|
|
28
|
+
currentEl.style.width = originStyle.width;
|
|
29
|
+
currentEl.style.height = originStyle.height;
|
|
30
|
+
currentEl.style.maxWidth = originStyle.maxWidth;
|
|
31
|
+
currentEl.style.maxHeight = originStyle.maxHeight;
|
|
32
|
+
currentEl.style.zIndex = originStyle.zIndex;
|
|
33
|
+
}
|
|
34
|
+
originStyle = undefined;
|
|
35
|
+
currentEl = undefined;
|
|
36
|
+
};
|
|
37
|
+
}
|
package/getAspectRatio.js
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
import{farey}from
|
|
1
|
+
import { farey } from '@js-toolkit/utils/farey';
|
|
2
|
+
/** Approximate aspect ratio */
|
|
3
|
+
export function getAspectRatio(width, height) {
|
|
4
|
+
const ratio = width / height;
|
|
5
|
+
// https://stackoverflow.com/a/43016456
|
|
6
|
+
const [rwidth, rheight] = farey(ratio, 50);
|
|
7
|
+
return { width: rwidth, height: rheight, ratio };
|
|
8
|
+
}
|
package/getBrowserLanguage.js
CHANGED
|
@@ -1 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
|
2
|
+
/**
|
|
3
|
+
* @param includeRegion subtag separated by `-`.
|
|
4
|
+
*/
|
|
5
|
+
export function getBrowserLanguage(includeRegion = false) {
|
|
6
|
+
const language = navigator.languages && navigator.languages.length > 0
|
|
7
|
+
? navigator.languages[0]
|
|
8
|
+
: navigator.language || navigator.userLanguage;
|
|
9
|
+
return includeRegion ? language : language.split('-')[0];
|
|
10
|
+
}
|
package/getCurrentScriptUrl.js
CHANGED
package/getEventAwaiter.js
CHANGED
|
@@ -1 +1,41 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable no-nested-ternary */
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import { getAwaiter } from '@js-toolkit/utils/getAwaiter';
|
|
4
|
+
import { EventEmitterListener } from './EventEmitterListener';
|
|
5
|
+
import { isEventEmitterLike, } from './EventEmitterListener.utils';
|
|
6
|
+
export function getEventAwaiter(target, resolveEvent, rejectEvent, { ignoreResolve, eventToError, lazy = true, ...rest } = {}) {
|
|
7
|
+
const readyAwaiter = getAwaiter({ lazy, ...rest });
|
|
8
|
+
const resolveEvents = Array.isArray(resolveEvent) ? resolveEvent : [resolveEvent];
|
|
9
|
+
const rejectEvents = rejectEvent == null ? [] : Array.isArray(rejectEvent) ? rejectEvent : [rejectEvent];
|
|
10
|
+
const listener = isEventEmitterLike(target) ? target : new EventEmitterListener(target);
|
|
11
|
+
const resolve = (ev) => {
|
|
12
|
+
const ignore = ignoreResolve ? ignoreResolve(ev) : false;
|
|
13
|
+
!ignore && readyAwaiter.resolve(ev);
|
|
14
|
+
};
|
|
15
|
+
const reject = (ev) => {
|
|
16
|
+
const err = eventToError ? eventToError(ev) : ev;
|
|
17
|
+
err != null && readyAwaiter.reject(err);
|
|
18
|
+
};
|
|
19
|
+
const destroy = () => {
|
|
20
|
+
resolveEvents.forEach((e) => listener.off(e, resolve));
|
|
21
|
+
rejectEvents.forEach((e) => listener.off(e, reject));
|
|
22
|
+
};
|
|
23
|
+
const resolveOrigin = readyAwaiter.resolve;
|
|
24
|
+
readyAwaiter.resolve = (...args) => {
|
|
25
|
+
destroy();
|
|
26
|
+
return resolveOrigin(...args);
|
|
27
|
+
};
|
|
28
|
+
const rejectOrigin = readyAwaiter.reject;
|
|
29
|
+
readyAwaiter.reject = (...args) => {
|
|
30
|
+
rejectOrigin(...args);
|
|
31
|
+
destroy();
|
|
32
|
+
};
|
|
33
|
+
// const waitOrigin = readyAwaiter.wait;
|
|
34
|
+
// readyAwaiter.wait = (...args) => {
|
|
35
|
+
// if (driver.isReady) readyAwaiter.resolve();
|
|
36
|
+
// return waitOrigin(...args);
|
|
37
|
+
// };
|
|
38
|
+
resolveEvents.forEach((e) => listener.on(e, resolve));
|
|
39
|
+
rejectEvents.forEach((e) => listener.on(e, reject));
|
|
40
|
+
return readyAwaiter;
|
|
41
|
+
}
|
package/getGeoCoordinates.js
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/prefer-promise-reject-errors */
|
|
2
|
+
export function getGeoCoordinates({ timeout = 10000, maximumAge = 60000, ...rest } = {}) {
|
|
3
|
+
return new Promise((resolve, reject) => {
|
|
4
|
+
navigator.geolocation.getCurrentPosition((pos) => resolve(pos), (err) => reject(err), { timeout, maximumAge, ...rest });
|
|
5
|
+
});
|
|
6
|
+
}
|
package/getGeoLocality.js
CHANGED
|
@@ -1 +1,19 @@
|
|
|
1
|
-
import{getErrorMessage}from
|
|
1
|
+
import { getErrorMessage } from '@js-toolkit/utils/getErrorMessage';
|
|
2
|
+
export function getGeoLocality({ longitude, latitude, lang }) {
|
|
3
|
+
const url = new URL('https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode');
|
|
4
|
+
url.searchParams.set('f', 'json');
|
|
5
|
+
url.searchParams.set('featureTypes', 'Locality');
|
|
6
|
+
url.searchParams.set('location', `{ x: ${longitude}, y: ${latitude} }`);
|
|
7
|
+
lang && url.searchParams.set('langCode', lang);
|
|
8
|
+
return window
|
|
9
|
+
.fetch(url.toString())
|
|
10
|
+
.then((res) => res.json())
|
|
11
|
+
.then((data) => {
|
|
12
|
+
/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
|
|
13
|
+
const { address, error } = data;
|
|
14
|
+
if (error != null)
|
|
15
|
+
throw new Error(getErrorMessage(error), { cause: error });
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
|
17
|
+
return (address.PlaceName || address.LongLabel || address.ShortLabel || '');
|
|
18
|
+
});
|
|
19
|
+
}
|
package/getInnerRect.js
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
import{getInnerXDimensions}from
|
|
1
|
+
import { getInnerXDimensions } from './getInnerXDimensions';
|
|
2
|
+
import { getInnerYDimensions } from './getInnerYDimensions';
|
|
3
|
+
export function getInnerRect(elementOrComputedStyle) {
|
|
4
|
+
const computedStyle = 'tagName' in elementOrComputedStyle
|
|
5
|
+
? window.getComputedStyle(elementOrComputedStyle)
|
|
6
|
+
: elementOrComputedStyle;
|
|
7
|
+
return Object.assign(getInnerXDimensions(computedStyle), getInnerYDimensions(computedStyle));
|
|
8
|
+
}
|
package/getInnerXDimensions.js
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
export function getInnerXDimensions(
|
|
1
|
+
export function getInnerXDimensions(elementOrComputedStyle) {
|
|
2
|
+
const { boxSizing, paddingLeft, paddingRight, borderLeftWidth, borderRightWidth, width } = 'tagName' in elementOrComputedStyle
|
|
3
|
+
? window.getComputedStyle(elementOrComputedStyle)
|
|
4
|
+
: elementOrComputedStyle;
|
|
5
|
+
const left = parseFloat(paddingLeft) + parseFloat(borderLeftWidth);
|
|
6
|
+
const right = parseFloat(paddingRight) + parseFloat(borderRightWidth);
|
|
7
|
+
const innerWidth = boxSizing === 'border-box' ? parseFloat(width) - left - right : parseFloat(width);
|
|
8
|
+
return { width: innerWidth, left, right };
|
|
9
|
+
}
|
package/getInnerYDimensions.js
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
export function getInnerYDimensions(
|
|
1
|
+
export function getInnerYDimensions(elementOrComputedStyle) {
|
|
2
|
+
const { boxSizing, paddingTop, paddingBottom, borderTopWidth, borderBottomWidth, height } = 'tagName' in elementOrComputedStyle
|
|
3
|
+
? window.getComputedStyle(elementOrComputedStyle)
|
|
4
|
+
: elementOrComputedStyle;
|
|
5
|
+
const top = parseFloat(paddingTop) + parseFloat(borderTopWidth);
|
|
6
|
+
const bottom = parseFloat(paddingBottom) + parseFloat(borderBottomWidth);
|
|
7
|
+
const innerHeight = boxSizing === 'border-box' ? parseFloat(height) - top - bottom : parseFloat(height);
|
|
8
|
+
return { height: innerHeight, top, bottom };
|
|
9
|
+
}
|
package/getPinchZoomHandlers.js
CHANGED
|
@@ -1 +1,134 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/strict-boolean-expressions */
|
|
2
|
+
import { preventDefault } from './preventDefault';
|
|
3
|
+
// Calculate distance between two fingers
|
|
4
|
+
function getDistanceBetweenFingers(touch1, touch2) {
|
|
5
|
+
return Math.hypot(touch2.pageX - touch1.pageX, touch2.pageY - touch1.pageY);
|
|
6
|
+
}
|
|
7
|
+
function getPoint(touch1, touch2) {
|
|
8
|
+
if (!touch2) {
|
|
9
|
+
return { x: touch1.pageX, y: touch1.pageY };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
x: (touch1.pageX + touch2.pageX) / 2,
|
|
13
|
+
y: (touch1.pageY + touch2.pageY) / 2,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function getPinchZoomHandlers({ canStart, onUpdate, onEnd, getBounds, acceleration = 2, }) {
|
|
17
|
+
const start = { x: 0, y: 0, distance: 0 };
|
|
18
|
+
let prevScale = 1;
|
|
19
|
+
let prevDeltaX = 0;
|
|
20
|
+
let prevDeltaY = 0;
|
|
21
|
+
let changed = false;
|
|
22
|
+
let state;
|
|
23
|
+
const begin = (event) => {
|
|
24
|
+
// If previous state is finished, reset changed flag.
|
|
25
|
+
if (!state) {
|
|
26
|
+
changed = false;
|
|
27
|
+
}
|
|
28
|
+
state = event.touches.length >= 2 ? 'zoom' : 'move';
|
|
29
|
+
prevDeltaX = 0;
|
|
30
|
+
prevDeltaY = 0;
|
|
31
|
+
if (state === 'zoom') {
|
|
32
|
+
prevScale = 1;
|
|
33
|
+
}
|
|
34
|
+
const touch1 = event.touches[0];
|
|
35
|
+
const touch2 = event.touches[1];
|
|
36
|
+
// Calculate where the fingers have started on the X and Y axis
|
|
37
|
+
Object.assign(start, getPoint(touch1, touch2));
|
|
38
|
+
start.distance = touch2 ? getDistanceBetweenFingers(touch1, touch2) : 0;
|
|
39
|
+
// console.log('begin', state, start, event);
|
|
40
|
+
};
|
|
41
|
+
const end = (x, y) => {
|
|
42
|
+
// console.log('end', correctedPoint);
|
|
43
|
+
state = undefined;
|
|
44
|
+
// changed = false;
|
|
45
|
+
onEnd(x, y);
|
|
46
|
+
};
|
|
47
|
+
const onTouchStart = (event) => {
|
|
48
|
+
if (event.touches.length > 2)
|
|
49
|
+
return;
|
|
50
|
+
if (!canStart(state ?? 'move', event))
|
|
51
|
+
return;
|
|
52
|
+
begin(event);
|
|
53
|
+
if (state === 'zoom') {
|
|
54
|
+
preventDefault(event); // Prevent click and so on
|
|
55
|
+
// stopPropagation(event);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
const onTouchMove = (event) => {
|
|
59
|
+
if (state == null)
|
|
60
|
+
return;
|
|
61
|
+
preventDefault(event); // Prevent page scroll
|
|
62
|
+
// stopPropagation(event);
|
|
63
|
+
const touch1 = event.touches[0];
|
|
64
|
+
const touch2 = event.touches[1];
|
|
65
|
+
// Calculate how much the fingers have moved on the X and Y axis
|
|
66
|
+
const point = getPoint(touch1, touch2);
|
|
67
|
+
const deltaX = (point.x - start.x) * acceleration; // * for accelarated movement
|
|
68
|
+
const deltaY = (point.y - start.y) * acceleration; // * for accelarated movement
|
|
69
|
+
const deltaOffsetX = deltaX - prevDeltaX;
|
|
70
|
+
const deltaOffsetY = deltaY - prevDeltaY;
|
|
71
|
+
prevDeltaX = deltaX;
|
|
72
|
+
prevDeltaY = deltaY;
|
|
73
|
+
changed = true;
|
|
74
|
+
if (touch2 && state === 'zoom') {
|
|
75
|
+
// Calculate the scale
|
|
76
|
+
const deltaDistance = getDistanceBetweenFingers(touch1, touch2);
|
|
77
|
+
const scale = deltaDistance / start.distance;
|
|
78
|
+
const deltaScale = scale - prevScale;
|
|
79
|
+
prevScale = scale;
|
|
80
|
+
// console.log(state, { scale, deltaScale, deltaX, deltaY, deltaOffsetX, deltaOffsetY });
|
|
81
|
+
onUpdate(deltaScale, deltaOffsetX, deltaOffsetY);
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// console.log(state, { deltaX, deltaY, deltaOffsetX, deltaOffsetY });
|
|
85
|
+
onUpdate(0, deltaOffsetX, deltaOffsetY);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
const onTouchEnd = (event) => {
|
|
89
|
+
// console.log('END', state, changed, event.touches.length, event);
|
|
90
|
+
if (!state)
|
|
91
|
+
return;
|
|
92
|
+
if (changed) {
|
|
93
|
+
preventDefault(event); // Prevent click and so on
|
|
94
|
+
// stopPropagation(event);
|
|
95
|
+
}
|
|
96
|
+
// If one finger remains, restart potential pan
|
|
97
|
+
if (event.touches.length === 1) {
|
|
98
|
+
begin(event);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (event.touches.length > 0)
|
|
102
|
+
return;
|
|
103
|
+
// Calculate offsets if out of bounds
|
|
104
|
+
if (getBounds && changed) {
|
|
105
|
+
const [targetRect, boundsRect] = getBounds();
|
|
106
|
+
// console.log('FIX', targetRect, boundsRect, window.scrollX, window.scrollY);
|
|
107
|
+
const byLeft = targetRect.left + window.scrollX > boundsRect.left;
|
|
108
|
+
const byTop = targetRect.top + window.scrollY > boundsRect.top;
|
|
109
|
+
const byRight = targetRect.right + window.scrollX < boundsRect.right;
|
|
110
|
+
const byBottom = targetRect.bottom + window.scrollY < boundsRect.bottom;
|
|
111
|
+
if (byLeft || byTop || byRight || byBottom) {
|
|
112
|
+
const x = byLeft || byRight
|
|
113
|
+
? ((targetRect.width - boundsRect.width) / 2 || 0) -
|
|
114
|
+
(byRight ? targetRect.width - boundsRect.width : 0)
|
|
115
|
+
: 0;
|
|
116
|
+
const y = byTop || byBottom
|
|
117
|
+
? ((targetRect.height - boundsRect.height) / 2 || 0) -
|
|
118
|
+
(byBottom ? targetRect.height - boundsRect.height : 0)
|
|
119
|
+
: 0;
|
|
120
|
+
end(x, y);
|
|
121
|
+
// console.log('FIX', x, y, byLeft, byTop, byRight, byBottom);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
end();
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
onTouchStart,
|
|
129
|
+
onTouchMove,
|
|
130
|
+
onTouchEnd,
|
|
131
|
+
getState: () => state,
|
|
132
|
+
// isChanged: () => changed,
|
|
133
|
+
};
|
|
134
|
+
}
|
package/getRandomID.js
CHANGED
|
@@ -1 +1,4 @@
|
|
|
1
|
-
export function getRandomID(
|
|
1
|
+
export function getRandomID(maxLength) {
|
|
2
|
+
const result = crypto.getRandomValues(new Uint32Array(1))[0].toString(16);
|
|
3
|
+
return maxLength != null && maxLength > 0 ? result.substring(0, maxLength) : result;
|
|
4
|
+
}
|
package/getScreenSize.js
CHANGED
|
@@ -1 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-deprecated */
|
|
2
|
+
function angleToOrientationType(angle) {
|
|
3
|
+
if (angle === 0)
|
|
4
|
+
return 'portrait-primary';
|
|
5
|
+
if (angle === 180)
|
|
6
|
+
return 'portrait-secondary';
|
|
7
|
+
if (angle === 90)
|
|
8
|
+
return 'landscape-primary';
|
|
9
|
+
if (angle === -90)
|
|
10
|
+
return 'landscape-secondary';
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
export function getScreenSize({ respectOrientation = true } = {}) {
|
|
14
|
+
const { width, height, orientation } = window.screen;
|
|
15
|
+
if (respectOrientation) {
|
|
16
|
+
const orientationType = orientation != null ? orientation.type : angleToOrientationType(window.orientation);
|
|
17
|
+
if ((orientationType === 'landscape-primary' || orientationType === 'landscape-secondary') &&
|
|
18
|
+
width < height) {
|
|
19
|
+
return { width: height, height: width };
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return { width, height };
|
|
23
|
+
}
|
package/getSecondsCounter.js
CHANGED
|
@@ -1 +1,47 @@
|
|
|
1
|
-
import{toInt}from
|
|
1
|
+
import { toInt } from '@js-toolkit/utils/toInt';
|
|
2
|
+
export function getSecondsCounter({ onChange } = {}) {
|
|
3
|
+
const timestamps = new Set();
|
|
4
|
+
let listener = onChange;
|
|
5
|
+
let destroyed = false;
|
|
6
|
+
return {
|
|
7
|
+
getTotal() {
|
|
8
|
+
return timestamps.size;
|
|
9
|
+
},
|
|
10
|
+
push(time) {
|
|
11
|
+
if (destroyed)
|
|
12
|
+
throw new Error('SecondsCounter was destroyed.');
|
|
13
|
+
const currentTime =
|
|
14
|
+
// eslint-disable-next-line no-nested-ternary
|
|
15
|
+
typeof time === 'number'
|
|
16
|
+
? time
|
|
17
|
+
: 'currentTime' in time
|
|
18
|
+
? time.currentTime
|
|
19
|
+
: time.data.currentTime;
|
|
20
|
+
if (time == null)
|
|
21
|
+
return;
|
|
22
|
+
// Add new seconds to list
|
|
23
|
+
const secs = toInt(currentTime);
|
|
24
|
+
if (secs > 0 && !timestamps.has(secs)) {
|
|
25
|
+
timestamps.add(secs);
|
|
26
|
+
listener?.({ value: secs, total: timestamps.size });
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
get onChange() {
|
|
30
|
+
return listener;
|
|
31
|
+
},
|
|
32
|
+
set onChange(value) {
|
|
33
|
+
listener = value;
|
|
34
|
+
},
|
|
35
|
+
reset() {
|
|
36
|
+
timestamps.clear();
|
|
37
|
+
},
|
|
38
|
+
destroy() {
|
|
39
|
+
destroyed = true;
|
|
40
|
+
listener = undefined;
|
|
41
|
+
this.reset();
|
|
42
|
+
},
|
|
43
|
+
[Symbol.dispose]() {
|
|
44
|
+
this.destroy();
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -1 +1,251 @@
|
|
|
1
|
-
import{v4 as uuid}
|
|
1
|
+
import { v4 as uuid } from 'uuid';
|
|
2
|
+
import log from '@js-toolkit/utils/log';
|
|
3
|
+
import { onDOMReady } from '../onDOMReady';
|
|
4
|
+
import { isPingMessage, isTargetReadyMessage, } from './messages';
|
|
5
|
+
import { isWindowProxy, readTargets } from './utils';
|
|
6
|
+
import { getOriginFromMessage } from './getOriginFromMessage';
|
|
7
|
+
export { getClientMessages, getHostMessages } from './messages';
|
|
8
|
+
// function createChannelProxy(channelMap: Map<string, MessageChannel>): AutoConnector.Channel {
|
|
9
|
+
// return {
|
|
10
|
+
// addEventListener(...args: Parameters<MessagePort['addEventListener']>) {
|
|
11
|
+
// channelMap.forEach(({ port1 }) => port1.addEventListener(...args));
|
|
12
|
+
// },
|
|
13
|
+
// removeEventListener(...args: Parameters<MessagePort['removeEventListener']>) {
|
|
14
|
+
// channelMap.forEach(({ port1 }) => port1.removeEventListener(...args));
|
|
15
|
+
// },
|
|
16
|
+
// start() {
|
|
17
|
+
// channelMap.forEach(({ port1 }) => port1.start());
|
|
18
|
+
// },
|
|
19
|
+
// close() {
|
|
20
|
+
// channelMap.forEach(({ port1 }) => port1.close());
|
|
21
|
+
// },
|
|
22
|
+
// postMessage(message, options, ...args) {
|
|
23
|
+
// channelMap.forEach(({ port1 }) => {
|
|
24
|
+
// port1.postMessage(message, options as StructuredSerializeOptions, ...args);
|
|
25
|
+
// });
|
|
26
|
+
// },
|
|
27
|
+
// };
|
|
28
|
+
// }
|
|
29
|
+
export function getAutoConnector({ id, label: labelOption, strictTargets = true, messagesTypes, channel: channelOption, logger = log.getLogger('AutoConnector'), onSendData, onConnect, }) {
|
|
30
|
+
const uid = id || uuid();
|
|
31
|
+
const label = labelOption || uid;
|
|
32
|
+
const msgMap = new Map();
|
|
33
|
+
const channelMap = channelOption === 'open' ? new Map() : undefined;
|
|
34
|
+
// let channel: AutoConnector.Channel | MessagePort | undefined;
|
|
35
|
+
let specialTargets;
|
|
36
|
+
let disposer;
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
|
|
38
|
+
const post = (message, target, origin, targetId, transfer) => {
|
|
39
|
+
if (window === target)
|
|
40
|
+
return;
|
|
41
|
+
if (isWindowProxy(target)) {
|
|
42
|
+
target.postMessage(message, origin, transfer);
|
|
43
|
+
const targetName = target === window.parent ? 'iframe parent' : 'iframe';
|
|
44
|
+
logger.v1(`${label}: Post message to ${targetName} (uid=${targetId},self.uid=${uid},origin=${origin}):`, message);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
target.postMessage(message, transfer && { transfer });
|
|
48
|
+
logger.v1(`${label}: Post message to MessageEventSource (uid=${targetId},self.uid=${uid}):`, message);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const sendPing = (target, origin, targetId) => {
|
|
52
|
+
post({ uid, type: messagesTypes.Ping }, target, origin, targetId);
|
|
53
|
+
};
|
|
54
|
+
const sendReady = (data, target, origin, targetId, port) => {
|
|
55
|
+
post({ uid, type: messagesTypes.SelfReady, data }, target, origin, targetId, port ? [port] : undefined);
|
|
56
|
+
};
|
|
57
|
+
const onReceiveMessage = (message) => {
|
|
58
|
+
if (!message.source || message.source === window) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (!isPingMessage(message.data, messagesTypes) &&
|
|
62
|
+
!isTargetReadyMessage(message.data, messagesTypes)) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (message.data.uid === uid) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const target = message.source;
|
|
69
|
+
const targetId = message.data.uid;
|
|
70
|
+
logger.v1(`${label}: Receive message from iframe (uid=${targetId},self.uid=${uid},origin=${message.origin}):`, message.data);
|
|
71
|
+
if (strictTargets && specialTargets && !specialTargets.has(target)) {
|
|
72
|
+
logger.v1(`${label}: Could not find target (uid=${targetId},self.uid=${uid}) by message.source.`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const origin = getOriginFromMessage(message);
|
|
76
|
+
// console.log(label, { ...msgMap.get(targetId) });
|
|
77
|
+
// Ping from iframe
|
|
78
|
+
if (isPingMessage(message.data, messagesTypes) && !msgMap.get(targetId)?.Ping) {
|
|
79
|
+
msgMap.set(targetId, { ...msgMap.get(targetId), Ping: true });
|
|
80
|
+
sendPing(target, origin, targetId);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// In order to wait messages queue in target before call `onConnect`.
|
|
84
|
+
let needAsync = false;
|
|
85
|
+
// Ping or Ready from iframe
|
|
86
|
+
if (msgMap.get(targetId)?.Ping && !msgMap.get(targetId)?.SelfReady) {
|
|
87
|
+
msgMap.set(targetId, { ...msgMap.get(targetId), SelfReady: true });
|
|
88
|
+
// if (!channel && channelMap) {
|
|
89
|
+
// channel = createChannelProxy(channelMap);
|
|
90
|
+
// }
|
|
91
|
+
const targetInfo = { target, origin };
|
|
92
|
+
const port2 = channelMap
|
|
93
|
+
? (() => {
|
|
94
|
+
const messageChannel = channelMap.get(targetId)?.[0] ?? new MessageChannel();
|
|
95
|
+
channelMap.set(targetId, [messageChannel, targetInfo]);
|
|
96
|
+
return messageChannel.port2;
|
|
97
|
+
})()
|
|
98
|
+
: undefined;
|
|
99
|
+
sendReady(onSendData ? onSendData(targetInfo) : undefined, target, origin, targetId, port2);
|
|
100
|
+
needAsync = false;
|
|
101
|
+
// return;
|
|
102
|
+
}
|
|
103
|
+
// Ready from iframe
|
|
104
|
+
if (isTargetReadyMessage(message.data, messagesTypes) &&
|
|
105
|
+
msgMap.get(targetId)?.SelfReady) {
|
|
106
|
+
// Reset state for target
|
|
107
|
+
msgMap.delete(targetId);
|
|
108
|
+
specialTargets?.delete(target);
|
|
109
|
+
const port = (() => {
|
|
110
|
+
if (channelOption === 'open') {
|
|
111
|
+
const port1 = channelMap?.get(targetId)?.[0].port1;
|
|
112
|
+
if (!port1)
|
|
113
|
+
throw new Error('Something went wrong: MessageChannel is not created despite the fact that the `channel` option is `open`.');
|
|
114
|
+
return port1;
|
|
115
|
+
}
|
|
116
|
+
if (channelOption === 'use') {
|
|
117
|
+
const port2 = message.ports[0];
|
|
118
|
+
if (port2 == null)
|
|
119
|
+
throw new Error('MessagePort is not received despite the fact that the `channel` option is `use`. The `channel` option of connector on another side must be equals `open`.');
|
|
120
|
+
return port2;
|
|
121
|
+
}
|
|
122
|
+
throw new Error(`Something went wrong: Unknown channelOption value ${channelOption}.`);
|
|
123
|
+
})();
|
|
124
|
+
const { data } = message.data;
|
|
125
|
+
const complete = () => {
|
|
126
|
+
logger.v1(`${label}: Connection established (self.uid=${uid} + uid=${targetId}).`);
|
|
127
|
+
onConnect({ data, target, origin }, port);
|
|
128
|
+
// if (channelOption === 'open') {
|
|
129
|
+
// const port1 = channelMap?.get(targetId)?.port1;
|
|
130
|
+
// if (!port1) throw new Error();
|
|
131
|
+
// onConnect({ data, target, origin }, port1);
|
|
132
|
+
// } else if (channelOption === 'use') {
|
|
133
|
+
// onConnect({ data, target, origin }, channel as MessagePort);
|
|
134
|
+
// } else if (!channelOption) {
|
|
135
|
+
// onConnect({ data, target, origin });
|
|
136
|
+
// }
|
|
137
|
+
};
|
|
138
|
+
if (needAsync)
|
|
139
|
+
setTimeout(complete, 0);
|
|
140
|
+
else
|
|
141
|
+
complete();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const stop = () => {
|
|
145
|
+
if (disposer) {
|
|
146
|
+
disposer();
|
|
147
|
+
disposer = undefined;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
const start = (targets, options = {}) => {
|
|
151
|
+
if (disposer && !options.append) {
|
|
152
|
+
logger.warn(`${label}: Already started. You should first call \`stop\`.`);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (options.append) {
|
|
156
|
+
const prevSpecialTargets = specialTargets;
|
|
157
|
+
stop();
|
|
158
|
+
specialTargets = prevSpecialTargets;
|
|
159
|
+
}
|
|
160
|
+
// Init and send ping to iframes
|
|
161
|
+
const ready = () => {
|
|
162
|
+
const newTargets = (() => {
|
|
163
|
+
const list = typeof targets === 'function' ? targets() : targets;
|
|
164
|
+
return list.length > 0 ? list : undefined;
|
|
165
|
+
})();
|
|
166
|
+
const newWindows = newTargets && readTargets(newTargets);
|
|
167
|
+
if (!newWindows)
|
|
168
|
+
return;
|
|
169
|
+
const newWindowsSet = new Set(newWindows);
|
|
170
|
+
if (options.append) {
|
|
171
|
+
specialTargets ??= new Set();
|
|
172
|
+
const t = specialTargets;
|
|
173
|
+
newWindowsSet.forEach((w) => t.add(w));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
specialTargets = newWindowsSet;
|
|
177
|
+
}
|
|
178
|
+
window.addEventListener('message', onReceiveMessage);
|
|
179
|
+
const frameSet = options.append ? newWindowsSet : specialTargets;
|
|
180
|
+
frameSet.forEach((frameWindow) => {
|
|
181
|
+
if (frameWindow !== window)
|
|
182
|
+
sendPing(frameWindow, '*', '');
|
|
183
|
+
});
|
|
184
|
+
};
|
|
185
|
+
// eslint-disable-next-line @typescript-eslint/no-confusing-void-expression
|
|
186
|
+
const cancel = typeof targets === 'function' ? onDOMReady(ready) : ready();
|
|
187
|
+
disposer = () => {
|
|
188
|
+
if (cancel)
|
|
189
|
+
cancel();
|
|
190
|
+
window.removeEventListener('message', onReceiveMessage);
|
|
191
|
+
specialTargets = undefined;
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
const close = (targets) => {
|
|
195
|
+
const set = targets instanceof Set ? targets : new Set(readTargets(targets));
|
|
196
|
+
if (set.size === 0)
|
|
197
|
+
return;
|
|
198
|
+
if (channelMap) {
|
|
199
|
+
channelMap.forEach(([ch, targetInfo], targetId) => {
|
|
200
|
+
if (set.has(targetInfo.target)) {
|
|
201
|
+
ch.port1.close();
|
|
202
|
+
ch.port2.close();
|
|
203
|
+
channelMap.delete(targetId);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
if (specialTargets) {
|
|
208
|
+
const list = specialTargets;
|
|
209
|
+
set.forEach((t) => list.delete(t));
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
start,
|
|
214
|
+
stop,
|
|
215
|
+
isStarted: () => !!disposer,
|
|
216
|
+
// getChannel: () => {
|
|
217
|
+
// if (!channel) {
|
|
218
|
+
// if (channelOption === 'use')
|
|
219
|
+
// throw new Error('Channel is unavailable. Maybe the connector is not connected yet.');
|
|
220
|
+
// else
|
|
221
|
+
// throw new Error(
|
|
222
|
+
// 'Channel is unavailable. In order to use channel the option `channel` must be one of `open`, `use`.'
|
|
223
|
+
// );
|
|
224
|
+
// }
|
|
225
|
+
// return channel;
|
|
226
|
+
// },
|
|
227
|
+
close,
|
|
228
|
+
destroy: () => {
|
|
229
|
+
stop();
|
|
230
|
+
msgMap.clear();
|
|
231
|
+
// if (channel) {
|
|
232
|
+
// channel.close();
|
|
233
|
+
// channel = undefined;
|
|
234
|
+
// }
|
|
235
|
+
if (channelMap) {
|
|
236
|
+
channelMap.forEach(([ch]) => {
|
|
237
|
+
ch.port1.close();
|
|
238
|
+
// ch.port1.onmessage = null;
|
|
239
|
+
// ch.port1.onmessageerror = null;
|
|
240
|
+
ch.port2.close();
|
|
241
|
+
// ch.port2.onmessage = null;
|
|
242
|
+
// ch.port2.onmessageerror = null;
|
|
243
|
+
});
|
|
244
|
+
channelMap.clear();
|
|
245
|
+
}
|
|
246
|
+
},
|
|
247
|
+
[Symbol.dispose]() {
|
|
248
|
+
this.destroy();
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|