@js-toolkit/web-utils 1.67.0 → 1.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/EventEmitterListener.d.ts +3 -3
  2. package/EventEmitterListener.js +222 -1
  3. package/EventEmitterListener.utils.d.ts +24 -13
  4. package/EventEmitterListener.utils.js +41 -1
  5. package/EventListeners.js +58 -1
  6. package/FullscreenController.js +193 -1
  7. package/README.md +159 -20
  8. package/WakeLockController.js +76 -1
  9. package/base64ToDataUrl.js +3 -1
  10. package/blobToDataUrl.js +10 -1
  11. package/checkPermission.js +8 -1
  12. package/copyToClipboard.js +37 -1
  13. package/createLoop.js +30 -1
  14. package/createRafLoop.js +56 -1
  15. package/dataUrlToBlob.js +13 -1
  16. package/fromBase64.js +10 -1
  17. package/fullscreen.js +167 -1
  18. package/fullscreenUtils.js +37 -1
  19. package/getAspectRatio.js +8 -1
  20. package/getBrowserLanguage.js +10 -1
  21. package/getCurrentScriptUrl.js +4 -1
  22. package/getEventAwaiter.js +41 -1
  23. package/getGeoCoordinates.js +6 -1
  24. package/getGeoLocality.js +19 -1
  25. package/getInnerRect.js +8 -1
  26. package/getInnerXDimensions.js +9 -1
  27. package/getInnerYDimensions.js +9 -1
  28. package/getPinchZoomHandlers.js +134 -1
  29. package/getRandomID.js +4 -1
  30. package/getScreenSize.js +23 -1
  31. package/getSecondsCounter.js +47 -1
  32. package/iframe/getAutoConnector.js +251 -1
  33. package/iframe/getOriginFromMessage.js +3 -1
  34. package/iframe/isIframeLoaded.js +9 -1
  35. package/iframe/messages.d.ts +2 -2
  36. package/iframe/messages.js +50 -1
  37. package/iframe/utils.js +33 -1
  38. package/imageToBlob.js +20 -1
  39. package/isImageTypeSupported.js +8 -1
  40. package/isWebPSupported.js +15 -1
  41. package/loadImage.js +29 -1
  42. package/loadScript.d.ts +1 -1
  43. package/loadScript.js +67 -1
  44. package/media/Capabilities.js +44 -1
  45. package/media/MediaNotAttachedError.d.ts +1 -1
  46. package/media/MediaNotAttachedError.js +6 -1
  47. package/media/MediaStreamController.js +84 -1
  48. package/media/PipController.d.ts +2 -2
  49. package/media/PipController.js +140 -1
  50. package/media/TextTracksController/TextTracksController.d.ts +0 -3
  51. package/media/TextTracksController/TextTracksController.js +251 -1
  52. package/media/TextTracksController/index.js +1 -1
  53. package/media/TextTracksController/utils.js +147 -1
  54. package/media/getDurationTime.js +3 -1
  55. package/media/getMediaSource.js +11 -1
  56. package/media/getSourceBuffer.js +5 -1
  57. package/media/isMediaSeekable.js +4 -1
  58. package/media/parseCueText.js +224 -1
  59. package/media/resetMedia.js +17 -1
  60. package/media/timeRanges.js +9 -1
  61. package/media/toggleNativeSubtitles.js +21 -1
  62. package/metrics/ga/DataLayerProxy.js +11 -1
  63. package/metrics/ga/getHandler.js +99 -1
  64. package/metrics/ga/types.js +1 -1
  65. package/metrics/types.js +1 -1
  66. package/metrics/yandex/DataLayerProxy.js +11 -1
  67. package/metrics/yandex/getHandler.js +63 -1
  68. package/metrics/yandex/types.js +2 -1
  69. package/onDOMReady.js +12 -1
  70. package/onPageReady.js +27 -1
  71. package/package.json +16 -13
  72. package/patchConsoleLogging.js +27 -1
  73. package/performance/getNavigationTiming.js +14 -1
  74. package/platform/Semver.js +23 -1
  75. package/platform/getChromeVersion.d.ts +2 -0
  76. package/platform/getChromeVersion.js +16 -0
  77. package/platform/getIOSVersion.js +18 -1
  78. package/platform/getPlatformInfo.js +49 -1
  79. package/platform/isAirPlayAvailable.js +3 -1
  80. package/platform/isAndroid.js +7 -1
  81. package/platform/isChrome.js +7 -1
  82. package/platform/isEMESupported.js +9 -1
  83. package/platform/isIOS.js +9 -1
  84. package/platform/isMSESupported.js +16 -1
  85. package/platform/isMacOS.js +12 -1
  86. package/platform/isMediaCapabilitiesSupported.js +6 -1
  87. package/platform/isMobile.js +16 -1
  88. package/platform/isMobileSimulation.js +7 -1
  89. package/platform/isSafari.js +14 -1
  90. package/platform/isScreenHDR.js +4 -1
  91. package/platform/isStandaloneApp.js +4 -1
  92. package/platform/isTelegramWebView.js +3 -1
  93. package/platform/isTouchSupported.js +4 -1
  94. package/preventDefault.d.ts +2 -2
  95. package/preventDefault.js +5 -1
  96. package/rafCallback.js +9 -1
  97. package/responsive/MediaQuery.js +40 -1
  98. package/responsive/MediaQueryListener.js +55 -1
  99. package/responsive/ViewSize.js +82 -1
  100. package/responsive/getViewSizeQueryMap.js +7 -1
  101. package/saveFileAs.js +22 -1
  102. package/serviceWorker/ServiceWorkerInstaller.d.ts +0 -1
  103. package/serviceWorker/ServiceWorkerInstaller.js +112 -1
  104. package/serviceWorker/utils.d.ts +1 -1
  105. package/serviceWorker/utils.js +86 -1
  106. package/stopPropagation.d.ts +2 -2
  107. package/stopPropagation.js +3 -1
  108. package/takeScreenshot.js +51 -1
  109. package/toBase64.js +9 -1
  110. package/toLocalPoint.js +9 -1
  111. package/types/index.js +2 -1
  112. package/types/refs.js +1 -1
  113. package/viewableTracker.js +69 -1
  114. package/webrtc/PeerConnection.js +212 -1
  115. package/webrtc/sdputils.js +417 -1
  116. package/ws/WSController.js +148 -1
@@ -1 +1,37 @@
1
- export function enterPseudoFullscreen(t){let e,i;return e={position:t.style.position,left:t.style.left,top:t.style.top,width:t.style.width,height:t.style.height,maxWidth:t.style.maxWidth,maxHeight:t.style.maxHeight,zIndex:t.style.zIndex},i=t,i.style.position="fixed",i.style.left="0px",i.style.top="0px",i.style.width="100%",i.style.height="100%",i.style.maxWidth="100%",i.style.maxHeight="100%",i.style.zIndex="99999",()=>{e&&i&&(i.style.position=e.position,i.style.left=e.left,i.style.top=e.top,i.style.width=e.width,i.style.height=e.height,i.style.maxWidth=e.maxWidth,i.style.maxHeight=e.maxHeight,i.style.zIndex=e.zIndex),e=void 0,i=void 0}}
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"@js-toolkit/utils/farey";export function getAspectRatio(t,o){const r=t/o,[e,i]=farey(r,50);return{width:e,height:i,ratio:r}}
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
+ }
@@ -1 +1,10 @@
1
- export function getBrowserLanguage(a=!1){const g=navigator.languages&&navigator.languages.length>0?navigator.languages[0]:navigator.language||navigator.userLanguage;return a?g:g.split("-")[0]}
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
+ }
@@ -1 +1,4 @@
1
- const{currentScript:currentScript}=document;export function getCurrentScriptUrl(r){return new URL(r??"",currentScript?.src)}
1
+ const { currentScript } = document;
2
+ export function getCurrentScriptUrl(url) {
3
+ return new URL(url ?? '', currentScript?.src);
4
+ }
@@ -1 +1,41 @@
1
- import{getAwaiter}from"@js-toolkit/utils/getAwaiter";import{EventEmitterListener}from"./EventEmitterListener";import{isEventEmitterLike}from"./EventEmitterListener.utils";export function getEventAwaiter(e,t,r,{ignoreResolve:i,eventToError:o,lazy:n=!0,...s}={}){const E=getAwaiter({lazy:n,...s}),a=Array.isArray(t)?t:[t],f=Array.isArray(r)&&r||r&&[r]||[],m=isEventEmitterLike(e)?e:new EventEmitterListener(e),v=e=>{!(!!i&&i(e))&&E.resolve(e)},c=e=>{const t=o?o(e):e;null!=t&&E.reject(t)},l=()=>{a.forEach(e=>m.off(e,v)),f.forEach(e=>m.off(e,c))},A=E.resolve;E.resolve=(...e)=>(l(),A(...e));const y=E.reject;return E.reject=(...e)=>{y(...e),l()},a.forEach(e=>m.on(e,v)),f.forEach(e=>m.on(e,c)),E}
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
+ }
@@ -1 +1,6 @@
1
- export function getGeoCoordinates({timeout:e=1e4,maximumAge:o=6e4,...t}={}){return new Promise((i,m)=>{navigator.geolocation.getCurrentPosition(e=>i(e),e=>m(e),{timeout:e,maximumAge:o,...t})})}
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"@js-toolkit/utils/getErrorMessage";export function getGeoLocality({longitude:e,latitude:r,lang:o}){const s=new URL("https://geocode.arcgis.com/arcgis/rest/services/World/GeocodeServer/reverseGeocode");return s.searchParams.set("f","json"),s.searchParams.set("featureTypes","Locality"),s.searchParams.set("location",`{ x: ${e}, y: ${r} }`),o&&s.searchParams.set("langCode",o),window.fetch(s.toString()).then(e=>e.json()).then(e=>{const{address:r,error:o}=e;if(o)throw new Error(getErrorMessage(o),{cause:o});return r.PlaceName||r.LongLabel||r.ShortLabel||""})}
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"./getInnerXDimensions";import{getInnerYDimensions}from"./getInnerYDimensions";export function getInnerRect(n){const e="tagName"in n?window.getComputedStyle(n):n;return Object.assign(getInnerXDimensions(e),getInnerYDimensions(e))}
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
+ }
@@ -1 +1,9 @@
1
- export function getInnerXDimensions(t){const{boxSizing:e,paddingLeft:o,paddingRight:r,borderLeftWidth:a,borderRightWidth:i,width:d}="tagName"in t?window.getComputedStyle(t):t,n=parseFloat(o)+parseFloat(a),p=parseFloat(r)+parseFloat(i);return{width:"border-box"===e?parseFloat(d)-n-p:parseFloat(d),left:n,right:p}}
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
+ }
@@ -1 +1,9 @@
1
- export function getInnerYDimensions(o){const{boxSizing:t,paddingTop:e,paddingBottom:a,borderTopWidth:r,borderBottomWidth:i,height:n}="tagName"in o?window.getComputedStyle(o):o,p=parseFloat(e)+parseFloat(r),d=parseFloat(a)+parseFloat(i);return{height:"border-box"===t?parseFloat(n)-p-d:parseFloat(n),top:p,bottom:d}}
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
+ }
@@ -1 +1,134 @@
1
- import{preventDefault}from"./preventDefault";function getDistanceBetweenFingers(e,t){return Math.hypot(t.pageX-e.pageX,t.pageY-e.pageY)}function getPoint(e,t){return t?{x:(e.pageX+t.pageX)/2,y:(e.pageY+t.pageY)/2}:{x:e.pageX,y:e.pageY}}export function getPinchZoomHandlers({canStart:e,onUpdate:t,onEnd:o,getBounds:n,acceleration:i=2}){const c={x:0,y:0,distance:0};let s,g=1,h=0,a=0,r=!1;const l=e=>{s||(r=!1),s=e.touches.length>=2?"zoom":"move",h=0,a=0,"zoom"===s&&(g=1);const t=e.touches[0],o=e.touches[1];Object.assign(c,getPoint(t,o)),c.distance=o?getDistanceBetweenFingers(t,o):0},u=(e,t)=>{s=void 0,o(e,t)};return{onTouchStart:t=>{t.touches.length>2||e(s??"move",t)&&(l(t),"zoom"===s&&preventDefault(t))},onTouchMove:e=>{if(null==s)return;preventDefault(e);const o=e.touches[0],n=e.touches[1],l=getPoint(o,n),u=(l.x-c.x)*i,p=(l.y-c.y)*i,d=u-h,f=p-a;if(h=u,a=p,r=!0,n&&"zoom"===s){const e=getDistanceBetweenFingers(o,n)/c.distance,i=e-g;g=e,t(i,d,f)}else t(0,d,f)},onTouchEnd:e=>{if(s)if(r&&preventDefault(e),1!==e.touches.length){if(!(e.touches.length>0)){if(n&&r){const[e,t]=n(),o=e.left+window.scrollX>t.left,i=e.top+window.scrollY>t.top,c=e.right+window.scrollX<t.right,s=e.bottom+window.scrollY<t.bottom;if(o||i||c||s){const n=o||c?((e.width-t.width)/2||0)-(c?e.width-t.width:0):0,g=i||s?((e.height-t.height)/2||0)-(s?e.height-t.height:0):0;return void u(n,g)}}u()}}else l(e)},getState:()=>s}}
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(t){const n=crypto.getRandomValues(new Uint32Array(1))[0].toString(16);return t&&t>0?n.substring(0,t):n}
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
- function angleToOrientationType(e){return 0===e?"portrait-primary":180===e?"portrait-secondary":90===e?"landscape-primary":-90===e?"landscape-secondary":void 0}export function getScreenSize({respectOrientation:e=!0}={}){const{width:n,height:t,orientation:i}=window.screen;if(e){const e=i?i.type:angleToOrientationType(window.orientation);if(("landscape-primary"===e||"landscape-secondary"===e)&&n<t)return{width:t,height:n}}return{width:n,height:t}}
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
+ }
@@ -1 +1,47 @@
1
- import{toInt}from"@js-toolkit/utils/toInt";export function getSecondsCounter({onChange:t}={}){const e=new Set;let o=t,n=!1;return{getTotal:()=>e.size,push(t){if(n)throw new Error("SecondsCounter was destroyed.");const r="number"==typeof t?t:"currentTime"in t?t.currentTime:t.data.currentTime;if(null==t)return;const s=toInt(r);s>0&&!e.has(s)&&(e.add(s),o&&o({value:s,total:e.size}))},get onChange(){return o},set onChange(t){o=t},reset(){e.clear()},destroy(){n=!0,o=void 0,this.reset()},[Symbol.dispose](){this.destroy()}}}
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}from"uuid";import log from"@js-toolkit/utils/log";import{onDOMReady}from"../onDOMReady";import{isPingMessage,isTargetReadyMessage}from"./messages";import{isWindowProxy,readTargets}from"./utils";import{getOriginFromMessage}from"./getOriginFromMessage";export{getClientMessages,getHostMessages}from"./messages";export function getAutoConnector({id:e,label:t,strictTargets:o=!0,messagesTypes:s,channel:n,logger:i=log.getLogger("AutoConnector"),onSendData:r,onConnect:a}){const d=e||uuid(),g=t||d,c=new Map,f="open"===n?new Map:void 0;let u,l;const p=(e,t,o,s,n)=>{if(window!==t)if(isWindowProxy(t)){t.postMessage(e,o,n);const r=t===window.parent?"iframe parent":"iframe";i.v1(`${g}: Post message to ${r} (uid=${s},self.uid=${d},origin=${o}):`,e)}else t.postMessage(e,n&&{transfer:n}),i.v1(`${g}: Post message to MessageEventSource (uid=${s},self.uid=${d}):`,e)},m=(e,t,o)=>{p({uid:d,type:s.Ping},e,t,o)},h=e=>{if(!e.source||e.source===window)return;if(!isPingMessage(e.data,s)&&!isTargetReadyMessage(e.data,s))return;if(e.data.uid===d)return;const t=e.source,l=e.data.uid;if(i.v1(`${g}: Receive message from iframe (uid=${l},self.uid=${d},origin=${e.origin}):`,e.data),o&&u&&!u.has(t))return void i.v1(`${g}: Could not find target (uid=${l},self.uid=${d}) by message.source.`);const h=getOriginFromMessage(e);if(isPingMessage(e.data,s)&&!c.get(l)?.Ping)return c.set(l,{...c.get(l),Ping:!0}),void m(t,h,l);let w=!1;if(c.get(l)?.Ping&&!c.get(l)?.SelfReady){c.set(l,{...c.get(l),SelfReady:!0});const e={target:t,origin:h},o=f?(()=>{const t=f.get(l)?.[0]??new MessageChannel;return f.set(l,[t,e]),t.port2})():void 0;((e,t,o,n,i)=>{p({uid:d,type:s.SelfReady,data:e},t,o,n,i?[i]:void 0)})(r?r(e):void 0,t,h,l,o),w=!1}if(isTargetReadyMessage(e.data,s)&&c.get(l)?.SelfReady){c.delete(l),u?.delete(t);const o=(()=>{if("open"===n){const e=f?.get(l)?.[0].port1;if(!e)throw new Error("Something went wrong: MessageChannel is not created despite the fact that the `channel` option is `open`.");return e}if("use"===n){const t=e.ports[0];if(!t)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`.");return t}})(),{data:s}=e.data,r=()=>{i.v1(`${g}: Connection established (self.uid=${d} + uid=${l}).`),a({data:s,target:t,origin:h},o)};w?setTimeout(r,0):r()}},w=()=>{l&&(l(),l=void 0)};return{start:(e,t={})=>{if(l&&!t.append)return void i.warn(`${g}: Already started. You should first call \`stop\`.`);if(t.append){const e=u;w(),u=e}const o=()=>{const o=(()=>{const t="function"==typeof e?e():e;return t.length>0?t:void 0})(),s=o&&readTargets(o);if(!s)return;const n=new Set(s);t.append?(u||(u=new Set),n.forEach(e=>u.add(e))):u=n,window.addEventListener("message",h);(t.append?n:u).forEach(e=>{e!==window&&m(e,"*","")})},s="function"==typeof e?onDOMReady(o):o();l=()=>{s&&s(),window.removeEventListener("message",h),u=void 0}},stop:w,isStarted:()=>!!l,close:e=>{const t=e instanceof Set?e:new Set(readTargets(e));if(0!==t.size&&(f&&f.forEach(([e,o],s)=>{t.has(o.target)&&(e.port1.close(),e.port2.close(),f.delete(s))}),u)){const e=u;t.forEach(t=>e.delete(t))}},destroy:()=>{w(),c.clear(),f&&(f.forEach(([e])=>{e.port1.close(),e.port2.close()}),f.clear())},[Symbol.dispose](){this.destroy()}}}
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
+ }