@js-toolkit/web-utils 1.52.0 → 1.54.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 (37) hide show
  1. package/EventEmitterListener.d.ts +2 -2
  2. package/EventEmitterListener.utils.d.ts +7 -4
  3. package/getBrowserLanguage.d.ts +4 -1
  4. package/getBrowserLanguage.js +1 -1
  5. package/getCurrentScriptUrl.d.ts +1 -0
  6. package/getCurrentScriptUrl.js +1 -0
  7. package/iframe/getAutoConnector.d.ts +7 -1
  8. package/iframe/getAutoConnector.js +1 -1
  9. package/iframe/utils.d.ts +1 -0
  10. package/iframe/utils.js +1 -1
  11. package/isWebPSupported.js +1 -1
  12. package/media/MediaNotAttachedError.js +1 -1
  13. package/media/TextTracksController/TextTracksController.d.ts +5 -3
  14. package/media/TextTracksController/TextTracksController.js +1 -1
  15. package/media/TextTracksController/utils.d.ts +8 -1
  16. package/media/TextTracksController/utils.js +1 -1
  17. package/media/getMediaSource.d.ts +2 -3
  18. package/media/getMediaSource.js +1 -1
  19. package/media/parseCueText.d.ts +16 -0
  20. package/media/parseCueText.js +1 -0
  21. package/onPageReady.d.ts +6 -2
  22. package/onPageReady.js +1 -1
  23. package/package.json +21 -15
  24. package/platform/getPlatformInfo.d.ts +0 -1
  25. package/platform/isMSESupported.d.ts +0 -5
  26. package/platform/isMSESupported.js +1 -1
  27. package/platform/isMobileSimulation.js +1 -1
  28. package/platform/isTouchSupported.d.ts +1 -0
  29. package/platform/isTouchSupported.js +1 -0
  30. package/serviceWorker/ServiceWorkerInstaller.d.ts +38 -0
  31. package/serviceWorker/ServiceWorkerInstaller.js +1 -0
  32. package/serviceWorker/utils.d.ts +14 -0
  33. package/serviceWorker/utils.js +1 -0
  34. package/viewableTracker.d.ts +12 -0
  35. package/viewableTracker.js +1 -0
  36. package/serviceWorker.d.ts +0 -8
  37. package/serviceWorker.js +0 -1
@@ -1,4 +1,4 @@
1
- import { type DomEventTarget, type GetEventMap as GetDomEventMap, type EmitterTarget, type GetEventType, type GetEventListener } from './EventEmitterListener.utils';
1
+ import { type DomEventTarget, type GetEventMap, type EmitterTarget, type GetEventType, type GetEventListener } from './EventEmitterListener.utils';
2
2
  type GetOnOptions<T extends EmitterTarget> = T extends DomEventTarget ? boolean | AddEventListenerOptions : unknown;
3
3
  type GetOnceOptions<T extends EmitterTarget> = T extends DomEventTarget ? boolean | OmitStrict<AddEventListenerOptions, 'once'> : undefined;
4
4
  type GetOffOptions<T extends EmitterTarget> = T extends DomEventTarget ? boolean | EventListenerOptions : undefined;
@@ -6,7 +6,7 @@ interface GetListenersOptions {
6
6
  event?: string | undefined;
7
7
  type?: 'normal' | 'capture' | undefined;
8
8
  }
9
- export declare class EventEmitterListener<T extends EmitterTarget, M extends AnyObject = GetDomEventMap<T>> {
9
+ export declare class EventEmitterListener<T extends EmitterTarget, M extends AnyObject = GetEventMap<T>> {
10
10
  readonly target: T;
11
11
  private readonly interceptor?;
12
12
  private readonly normalListeners;
@@ -1,3 +1,4 @@
1
+ import type { DataEventEmitter } from '@js-toolkit/utils/DataEventEmitter';
1
2
  export type DomEventTarget = EventTarget;
2
3
  export type EventTargetLike = {
3
4
  addEventListener: EventEmitterLike['on'];
@@ -19,12 +20,14 @@ export type GetEventType<T extends EmitterTarget> = T extends DomEventTarget ? G
19
20
  } ? K : string : T extends EventTargetLike ? T['addEventListener'] extends {
20
21
  (type: infer K, listener: AnyFunction, ...rest: unknown[]): unknown;
21
22
  } ? K : string : string;
22
- export type GetEventListener<T extends EmitterTarget, E, EM extends AnyObject = GetEventMap<T>> = T extends DomEventTarget ? IfExtends<EM, EmptyObject, EventListener, GetDomEventListener<E, EM>> : IfExtends<EM, EmptyObject, Parameters<T extends EventTargetLike ? T['addEventListener'] : T extends EventEmitterLike ? T['on'] : AnyFunction>['1'], (event: E extends keyof EM ? EM[E] : unknown, ...rest: any[]) => unknown>;
23
- export type GetEventMap<T> = 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 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 {
24
- EventMap: Record<string, any>;
25
- } ? T['EventMap'] : EmptyObject;
23
+ export type GetEventListener<T extends EmitterTarget, E, EM extends AnyObject = GetEventMap<T>> = T extends DomEventTarget ? IfExtends<EM, EmptyObject, EventListener, GetDomEventListener<E, EM>> : IfExtends<EM, EmptyObject, Parameters<T extends EventEmitterLike ? T['on'] : T extends EventTargetLike ? T['addEventListener'] : AnyFunction>['1'], (event: E extends keyof EM ? EM[E] : unknown, ...rest: any[]) => unknown>;
24
+ type ListenersMapToEventMap<T extends Record<string, AnyFunction[]>> = {
25
+ [P in keyof T]: Parameters<T[P][number]>[0];
26
+ };
27
+ export type GetEventMap<T> = 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 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 DataEventEmitter<any, any> ? ListenersMapToEventMap<ReturnType<T['listenersMap']>> : EmptyObject;
26
28
  export declare function isEventTargetLike(target: EmitterTarget): target is EventTargetLike;
27
29
  export declare function isDomEventTarget(target: EmitterTarget): target is DomEventTarget;
28
30
  export declare function isEventEmitterLike(target: EmitterTarget): target is EventEmitterLike;
29
31
  export declare function isPassiveSupported(): boolean;
30
32
  export declare function normalizeOptions(options: boolean | AddEventListenerOptions | undefined): typeof options;
33
+ export {};
@@ -3,4 +3,7 @@ declare global {
3
3
  userLanguage: string;
4
4
  }
5
5
  }
6
- export declare function getBrowserLanguage(): string | undefined;
6
+ /**
7
+ * @param includeRegion subtag separated by `-`.
8
+ */
9
+ export declare function getBrowserLanguage(includeRegion?: boolean): string;
@@ -1 +1 @@
1
- export function getBrowserLanguage(){if("undefined"!=typeof window&&"navigator"in window){return(navigator.languages&&navigator.languages.length>0?navigator.languages[0]:navigator.language||navigator.userLanguage).split("-")[0]}}
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]}
@@ -0,0 +1 @@
1
+ export declare function getCurrentScriptUrl(url?: string | URL | undefined): URL;
@@ -0,0 +1 @@
1
+ const{currentScript:currentScript}=document;export function getCurrentScriptUrl(r){return new URL(null!=r?r:"",null==currentScript?void 0:currentScript.src)}
@@ -1,10 +1,16 @@
1
1
  import { type MessagesTypes } from './messages';
2
2
  import { type Target } from './utils';
3
3
  export { getClientMessages, getHostMessages } from './messages';
4
+ interface StartOptions {
5
+ readonly append?: boolean;
6
+ }
4
7
  interface AutoConnector {
5
8
  /** If function passed the connector waits until DOM ready. */
6
- readonly start: (targets: ArrayLike<Target> | (() => ArrayLike<Target>)) => void;
9
+ readonly start: (targets: ArrayLike<Target> | (() => ArrayLike<Target>), options?: StartOptions) => void;
10
+ /** Stops a receiving new connections. */
7
11
  readonly stop: VoidFunction;
12
+ readonly isStarted: () => boolean;
13
+ readonly close: (targets: ArrayLike<Target>) => void;
8
14
  readonly destroy: VoidFunction;
9
15
  }
10
16
  interface TargetInfo {
@@ -1 +1 @@
1
- import{v4 as uuid}from"uuid";import{onDOMReady}from"../onDOMReady";import{isPingMessage,isTargetReadyMessage}from"./messages";import{findTarget,isWindowProxy}from"./utils";import{getOriginFromMessage}from"./getOriginFromMessage";export{getClientMessages,getHostMessages}from"./messages";export function getAutoConnector({id:e,label:t,strictTargets:o=!0,messagesTypes:n,channel:s,logger:i=console,onSendData:a,onConnect:r}){const d=e||uuid(),g=t||d,u=new Map,c="open"===s?new Map:void 0;let l,f;const m=(e,t,o,n,s)=>{window!==t&&(isWindowProxy(t)?(t.postMessage(e,o,s),i.debug(`${g}: Post message to iframe (uid=${n},origin=${o}):`,e)):(t.postMessage(e,s&&{transfer:s}),i.debug(`${g}: Post message to MessageEventSource (uid=${n}):`,e)))},p=(e,t,o)=>{m({uid:d,type:n.Ping},e,t,o)},v=e=>{var t,f,v,w;if(!e.source||e.source===window)return;if(!isPingMessage(e.data,n)&&!isTargetReadyMessage(e.data,n))return;if(e.data.uid===d)return;const M=e.source,h=e.data.uid;if(i.debug(`${g}: Receive message from iframe (uid=${h},origin=${e.origin}):`,e.data),o&&l){if(!findTarget(M,l))return void i.warn(`${g}: Could not find target (uid=${h}) by message.source.`)}const y=getOriginFromMessage(e);if(isPingMessage(e.data,n)&&!(null===(t=u.get(h))||void 0===t?void 0:t.Ping))return u.set(h,Object.assign(Object.assign({},u.get(h)),{Ping:!0})),void p(M,y,h);let $=!1;if((null===(f=u.get(h))||void 0===f?void 0:f.Ping)&&!(null===(v=u.get(h))||void 0===v?void 0:v.SelfReady)){u.set(h,Object.assign(Object.assign({},u.get(h)),{SelfReady:!0}));const e=c?(()=>{var e;const t=null!==(e=c.get(h))&&void 0!==e?e:new MessageChannel;return c.set(h,t),t.port2})():void 0;((e,t,o,s,i)=>{m({uid:d,type:n.SelfReady,data:e},t,o,s,i?[i]:void 0)})(a?a({target:M,origin:y}):void 0,M,y,h,e),$=!1}if(isTargetReadyMessage(e.data,n)&&(null===(w=u.get(h))||void 0===w?void 0:w.SelfReady)){u.delete(h);const t=(()=>{var t;if("open"===s){const e=null===(t=null==c?void 0:c.get(h))||void 0===t?void 0:t.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"===s){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:o}=e.data,n=()=>{i.debug(`${g}: Iframe connection established (${d} + ${h}).`),r({data:o,target:M,origin:y},t)};$?setTimeout(n,0):n()}},w=()=>{f&&(f(),f=void 0)};return{start:e=>{if(f)return void i.warn(`${g}: Already started. You should first call \`stop\`.`);const t=()=>{l=(()=>{const t="function"==typeof e?e():e;return t.length>0?t:void 0})(),window.addEventListener("message",v);const t=null!=l?l:[];for(let e=0;e<t.length;e+=1){const o=t[e],n=o instanceof HTMLIFrameElement?o.contentWindow:o;n&&n!==window&&p(n,"*","")}},o="function"==typeof e?onDOMReady(t):t();f=()=>{o&&o(),window.removeEventListener("message",v),l=void 0}},stop:w,destroy:()=>{w(),u.clear(),c&&(c.forEach((e=>{e.port1.close(),e.port2.close()})),c.clear())}}}
1
+ import{v4 as uuid}from"uuid";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=console,onSendData:a,onConnect:r}){const d=e||uuid(),g=t||d,u=new Map,l="open"===n?new Map:void 0;let c,f;const p=(e,t,o,s,n)=>{if(window!==t)if(isWindowProxy(t)){t.postMessage(e,o,n);const a=t===window.parent?"iframe parent":"iframe";i.debug(`${g}: Post message to ${a} (uid=${s},self.uid=${d},origin=${o}):`,e)}else t.postMessage(e,n&&{transfer:n}),i.debug(`${g}: Post message to MessageEventSource (uid=${s},self.uid=${d}):`,e)},v=(e,t,o)=>{p({uid:d,type:s.Ping},e,t,o)},m=e=>{var t,f,m,w;if(!e.source||e.source===window)return;if(!isPingMessage(e.data,s)&&!isTargetReadyMessage(e.data,s))return;if(e.data.uid===d)return;const h=e.source,M=e.data.uid;if(i.debug(`${g}: Receive message from iframe (uid=${M},self.uid=${d},origin=${e.origin}):`,e.data),o&&c&&!c.has(h))return void i.warn(`${g}: Could not find target (uid=${M},self.uid=${d}) by message.source.`);const y=getOriginFromMessage(e);if(isPingMessage(e.data,s)&&!(null===(t=u.get(M))||void 0===t?void 0:t.Ping))return u.set(M,Object.assign(Object.assign({},u.get(M)),{Ping:!0})),void v(h,y,M);let $=!1;if((null===(f=u.get(M))||void 0===f?void 0:f.Ping)&&!(null===(m=u.get(M))||void 0===m?void 0:m.SelfReady)){u.set(M,Object.assign(Object.assign({},u.get(M)),{SelfReady:!0}));const e={target:h,origin:y},t=l?(()=>{var t,o;const s=null!==(o=null===(t=l.get(M))||void 0===t?void 0:t[0])&&void 0!==o?o:new MessageChannel;return l.set(M,[s,e]),s.port2})():void 0;((e,t,o,n,i)=>{p({uid:d,type:s.SelfReady,data:e},t,o,n,i?[i]:void 0)})(a?a(e):void 0,h,y,M,t),$=!1}if(isTargetReadyMessage(e.data,s)&&(null===(w=u.get(M))||void 0===w?void 0:w.SelfReady)){u.delete(M),null==c||c.delete(h);const t=(()=>{var t;if("open"===n){const e=null===(t=null==l?void 0:l.get(M))||void 0===t?void 0:t[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:o}=e.data,s=()=>{i.debug(`${g}: Connection established (self.uid=${d} + uid=${M}).`),r({data:o,target:h,origin:y},t)};$?setTimeout(s,0):s()}},w=()=>{f&&(f(),f=void 0)};return{start:(e,t={})=>{if(f&&!t.append)return void i.warn(`${g}: Already started. You should first call \`stop\`.`);if(t.append){const e=c;w(),c=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?(c||(c=new Set),n.forEach((e=>c.add(e)))):c=n,window.addEventListener("message",m);(t.append?n:c).forEach((e=>{e!==window&&v(e,"*","")}))},s="function"==typeof e?onDOMReady(o):o();f=()=>{s&&s(),window.removeEventListener("message",m),c=void 0}},stop:w,isStarted:()=>!!f,close:e=>{const t=e instanceof Set?e:new Set(readTargets(e));if(0!==t.size&&(l&&l.forEach((([e,o],s)=>{t.has(o.target)&&(e.port1.close(),e.port2.close(),l.delete(s))})),c)){const e=c;t.forEach((t=>e.delete(t)))}},destroy:()=>{w(),u.clear(),l&&(l.forEach((([e])=>{e.port1.close(),e.port2.close()})),l.clear())}}}
package/iframe/utils.d.ts CHANGED
@@ -2,3 +2,4 @@ export type Target = HTMLIFrameElement | Window;
2
2
  export declare function selectFrames(): HTMLCollectionOf<HTMLIFrameElement>;
3
3
  export declare function findTarget<T extends Target>(source: Window, targets: ArrayLike<T>): T | undefined;
4
4
  export declare function isWindowProxy(target: MessageEventSource): target is Window;
5
+ export declare function readTargets(list: ArrayLike<Target>): Window[];
package/iframe/utils.js CHANGED
@@ -1 +1 @@
1
- export function selectFrames(){return document.getElementsByTagName("iframe")}export function findTarget(e,n){for(let t=0;t<n.length;t+=1){const o=n[t];if((o instanceof HTMLIFrameElement?o.contentWindow:o)===e)return o}}export function isWindowProxy(e){return!(void 0!==window.MessagePort&&e instanceof MessagePort||void 0!==window.ServiceWorker&&e instanceof ServiceWorker)}
1
+ export function selectFrames(){return document.getElementsByTagName("iframe")}export function findTarget(e,n){for(let t=0;t<n.length;t+=1){const o=n[t];if((o instanceof HTMLIFrameElement?o.contentWindow:o)===e)return o}}export function isWindowProxy(e){return!(void 0!==window.MessagePort&&e instanceof MessagePort||void 0!==window.ServiceWorker&&e instanceof ServiceWorker)}export function readTargets(e){const n=new Array;for(let t=0;t<e.length;t+=1){const o=e[t],r=o instanceof HTMLIFrameElement?o.contentWindow:o;r&&n.push(r)}return n}
@@ -1 +1 @@
1
- import{__awaiter}from"tslib";export function isWebPSupported(A="lossy"){return __awaiter(this,void 0,void 0,(function*(){const o={lossy:"UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",lossless:"UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",animation:"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"},i=new Image;return new Promise((Q=>{i.onload=()=>Q(i.width>0&&i.height>0),i.onerror=()=>Q(!1),i.src=`data:image/webp;base64,${o[A]}`}))}))}
1
+ import{__awaiter}from"tslib";export function isWebPSupported(){return __awaiter(this,arguments,void 0,(function*(A="lossy"){const o={lossy:"UklGRiIAAABXRUJQVlA4IBYAAAAwAQCdASoBAAEADsD+JaQAA3AAAAAA",lossless:"UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==",alpha:"UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAARBxAR/Q9ERP8DAABWUDggGAAAABQBAJ0BKgEAAQAAAP4AAA3AAP7mtQAAAA==",animation:"UklGRlIAAABXRUJQVlA4WAoAAAASAAAAAAAAAAAAQU5JTQYAAAD/////AABBTk1GJgAAAAAAAAAAAAAAAAAAAGQAAABWUDhMDQAAAC8AAAAQBxAREYiI/gcA"},Q=new Image;return new Promise((i=>{Q.onload=()=>i(Q.width>0&&Q.height>0),Q.onerror=()=>i(!1),Q.src=`data:image/webp;base64,${o[A]}`}))}))}
@@ -1 +1 @@
1
- import{ErrorCompat}from"@js-toolkit/utils/ErrorCompat";export class MediaNotAttachedError extends ErrorCompat{constructor(t="Media element is not attached yet.",r=void 0){super(MediaNotAttachedError,t,r)}}
1
+ import{ErrorCompat}from"@js-toolkit/utils/ErrorCompat";export class MediaNotAttachedError extends ErrorCompat{constructor(t="Media element is not attached yet.",r=void 0){super(MediaNotAttachedError,t,Object.assign(Object.assign({},r),{name:"MediaNotAttachedError "}))}}
@@ -10,9 +10,8 @@ interface TextTracksEventMap {
10
10
  texttrackchange: CustomEvent<TextTracksController.EventMap[TextTracksController.Events.CurrentTextTrackChanged][0]>;
11
11
  texttrackcuechange: CustomEvent<TextTracksController.EventMap[TextTracksController.Events.TextTrackCueChanged][0]>;
12
12
  }
13
- export interface Cue {
14
- id: string;
15
- text: string;
13
+ export interface Cue extends PartialBut<OmitStrict<VTTCue, keyof EventTarget | 'onenter' | 'onexit' | 'track' | 'pauseOnExit'>, 'id' | 'text' | 'startTime'> {
14
+ readonly rows: string[];
16
15
  }
17
16
  export type { TextTrackInfo, ActivateTextTrackInfo, TextTrackItem };
18
17
  export declare class TextTracksController extends EventEmitter<TextTracksController.EventMap> implements BaseMediaController {
@@ -40,6 +39,8 @@ export declare namespace TextTracksController {
40
39
  readonly emitNativeEvents?: boolean | undefined;
41
40
  /** Default `true`. */
42
41
  readonly hideActiveTrack?: boolean | undefined;
42
+ /** */
43
+ readonly preferCueRowLength?: number | undefined;
43
44
  }
44
45
  enum Events {
45
46
  TextTrackListChanged = "TrackListChanged",
@@ -60,6 +61,7 @@ export declare namespace TextTracksController {
60
61
  ];
61
62
  [Events.TextTrackCueChanged]: [
62
63
  {
64
+ readonly textTrack: TextTrack;
63
65
  readonly cues: readonly Cue[];
64
66
  }
65
67
  ];
@@ -1 +1 @@
1
- import{EventEmitter}from"eventemitter3";import{EventListeners}from"../../EventListeners";import{MediaNotAttachedError}from"../MediaNotAttachedError";import{parseTextTracks,setActiveTextTrack,addTextTracks,isIOSFullscreen}from"./utils";function dispatchNativeEvent(t,e,i){t.dispatchEvent(new CustomEvent(e,{detail:i}))}export class TextTracksController extends EventEmitter{get Events(){return TextTracksController.Events}constructor(t){var e,i;super(),this.eventListeners=new EventListeners,this.addedTracks=[],this.textTrackList=[],this.options=Object.assign(Object.assign({},t),{emitNativeEvents:null===(e=null==t?void 0:t.emitNativeEvents)||void 0===e||e,hideActiveTrack:null===(i=null==t?void 0:t.hideActiveTrack)||void 0===i||i})}isAttached(){return!!this.media}getMediaElement(){if(!this.media)throw new MediaNotAttachedError;return this.media}detach(){this.eventListeners.removeAllListeners(),this.media=void 0,this.addedTracks=[],this.textTrackList=[],this.textTrack=void 0,this.nextTextTrack=void 0}attach(t){this.detach(),this.media=t;const{textTracks:e}=t;let i=!1;const s=e=>{const{activeCues:i}=e.target;if(!i)return;const s=new Array(i.length);for(let t=0;t<s.length;t+=1)s[t]={id:i[t].id||String(t),text:i[t].text};this.emit(this.Events.TextTrackCueChanged,{cues:s}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackcuechange",{cues:s})},a=()=>{var e;setActiveTextTrack(t,null!==(e=this.textTrack)&&void 0!==e?e:this.nextTextTrack,this.options.hideActiveTrack||isIOSFullscreen(t)),this.textTrackList=parseTextTracks(t),this.emit(this.Events.TextTrackListChanged,{textTracks:this.textTrackList}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttracklistchange",{textTracks:this.textTrackList})},n=t=>{i||a(),this.eventListeners.scope(t).on("cuechange",s)},r=t=>{a(),this.eventListeners.scope(t).off("cuechange",s)};if(this.eventListeners.scope(e).on("change",(()=>{var e,i,s;const{textTracks:a}=t,{nextTextTrack:n}=this;let r=-1;for(let e=0;e<a.length;e+=1){const i=a[e];if(i.language===(null==n?void 0:n.language)&&i.kind===(null==n?void 0:n.kind)||isIOSFullscreen(t)||(i.mode="disabled"),"disabled"!==i.mode&&r>=0&&(i.mode="disabled"),"disabled"!==i.mode){if(r=e,!i.native&&!isIOSFullscreen(t)){const t=this.options.hideActiveTrack?"hidden":"showing";i.mode!==t&&(i.mode=t)}i.native&&i.language===(null==n?void 0:n.language)&&i.kind===n.kind&&(i.mode="showing")}}const c=a[r]&&(null!==(e=this.textTrackList[r])&&void 0!==e?e:{type:a[r].kind,lang:a[r].language,label:a[r].label});(null===(i=this.textTrack)||void 0===i?void 0:i.language)===(null==c?void 0:c.language)&&(null===(s=this.textTrack)||void 0===s?void 0:s.kind)===(null==c?void 0:c.kind)||(this.textTrack=c,this.nextTextTrack=this.textTrack,this.emit(this.Events.CurrentTextTrackChanged,{textTrack:this.textTrack,index:r}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackchange",{textTrack:this.textTrack,index:r}))})).on("addtrack",(({track:t})=>t&&n(t))).on("removetrack",(({track:t})=>t&&r(t))),e.length>0){i=!0;try{Array.prototype.forEach.call(e,n),a()}finally{i=!1}}}setTextTracks(t){const e=this.getMediaElement();if(this.eventListeners.scope(e,"@@setTextTracks").removeAllListeners(),this.addedTracks.forEach((t=>t.remove())),0===t.length)return;const i=()=>{addTextTracks(e,t,(t=>this.addedTracks.push(t)))};e.readyState>=e.HAVE_CURRENT_DATA?i():this.eventListeners.scope(e,"@@setTextTracks").once("loadeddata",i)}getActiveTextTrack(){return this.textTrack}setActiveTextTrack(t){const e=this.getMediaElement();this.nextTextTrack=t&&this.textTrackList.find((e=>e.language===t.language&&(!t.kind||e.kind===t.kind))),setActiveTextTrack(e,this.nextTextTrack,this.options.hideActiveTrack||isIOSFullscreen(e))}destroy(){this.detach(),this.removeAllListeners()}}!function(t){let e;!function(t){t.TextTrackListChanged="TrackListChanged",t.CurrentTextTrackChanged="CurrentTextTrackChanged",t.TextTrackCueChanged="TextTrackCueChanged"}(e=t.Events||(t.Events={}))}(TextTracksController||(TextTracksController={}));
1
+ import{EventEmitter}from"eventemitter3";import{EventListeners}from"../../EventListeners";import{MediaNotAttachedError}from"../MediaNotAttachedError";import{parseTextTracks,setActiveTextTrack,addTextTracks,isIOSFullscreen,splitRows,buildCueId}from"./utils";function dispatchNativeEvent(t,e,i){t.dispatchEvent(new CustomEvent(e,{detail:i}))}export class TextTracksController extends EventEmitter{get Events(){return TextTracksController.Events}constructor(t){var e,i,s;super(),this.eventListeners=new EventListeners,this.addedTracks=[],this.textTrackList=[],this.options=Object.assign(Object.assign({},t),{emitNativeEvents:null===(e=null==t?void 0:t.emitNativeEvents)||void 0===e||e,hideActiveTrack:null===(i=null==t?void 0:t.hideActiveTrack)||void 0===i||i,preferCueRowLength:null!==(s=null==t?void 0:t.preferCueRowLength)&&void 0!==s?s:0})}isAttached(){return!!this.media}getMediaElement(){if(!this.media)throw new MediaNotAttachedError;return this.media}detach(){this.eventListeners.removeAllListeners(),this.media=void 0,this.addedTracks=[],this.textTrackList=[],this.textTrack=void 0,this.nextTextTrack=void 0}attach(t){this.detach(),this.media=t;const{textTracks:e}=t;let i=!1;const s=(()=>{const e=[];return i=>{var s;const{activeCues:n}=i.target;if(!n)return;let a=e.length!==n.length;const r=new Array(n.length);for(let t=0;t<r.length;t+=1){const i=n[t];i.id=i.id||buildCueId(i,t),r[t]=i,r[t].rows=splitRows(i.text,this.options.preferCueRowLength),a||(null===(s=e[t])||void 0===s?void 0:s.id)===i.id||(a=!0),e[t]=i}e.length>r.length&&e.splice(r.length-e.length),a&&(this.emit(this.Events.TextTrackCueChanged,{textTrack:i.target,cues:r}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackcuechange",{textTrack:i.target,cues:r}))}})(),n=()=>{var e;this.textTrackList=parseTextTracks(t),this.emit(this.Events.TextTrackListChanged,{textTracks:this.textTrackList}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttracklistchange",{textTracks:this.textTrackList}),setActiveTextTrack(t,null!==(e=this.textTrack)&&void 0!==e?e:this.nextTextTrack,this.options.hideActiveTrack||isIOSFullscreen(t))},a=t=>{i||n(),this.eventListeners.scope(t).on("cuechange",s)},r=t=>{n(),this.eventListeners.scope(t).off("cuechange",s)};if(this.eventListeners.scope(e).on("change",(()=>{var e,i,s;const{textTracks:n}=t,{nextTextTrack:a}=this;let r=-1;for(let e=0;e<n.length;e+=1){const i=n[e];if(i.language===(null==a?void 0:a.language)&&i.kind===(null==a?void 0:a.kind)||isIOSFullscreen(t)||(i.mode="disabled"),"disabled"!==i.mode&&r>=0&&(i.mode="disabled"),"disabled"!==i.mode){if(r=e,!i.native&&!isIOSFullscreen(t)){const t=this.options.hideActiveTrack?"hidden":"showing";i.mode!==t&&(i.mode=t)}i.native&&i.language===(null==a?void 0:a.language)&&i.kind===a.kind&&(i.mode="showing")}}const c=n[r]&&(null!==(e=this.textTrackList[r])&&void 0!==e?e:{id:n[r].id,kind:n[r].kind,language:n[r].language,label:n[r].label});(null===(i=this.textTrack)||void 0===i?void 0:i.language)===(null==c?void 0:c.language)&&(null===(s=this.textTrack)||void 0===s?void 0:s.kind)===(null==c?void 0:c.kind)||(this.textTrack=c,this.nextTextTrack=this.textTrack,this.emit(this.Events.CurrentTextTrackChanged,{textTrack:this.textTrack,index:r}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackchange",{textTrack:this.textTrack,index:r}))})).on("addtrack",(({track:t})=>t&&a(t))).on("removetrack",(({track:t})=>t&&r(t))),e.length>0){i=!0;try{Array.prototype.forEach.call(e,a),n()}finally{i=!1}}}setTextTracks(t){const e=this.getMediaElement();if(this.eventListeners.scope(e,"@@setTextTracks").removeAllListeners(),this.addedTracks.forEach((t=>t.remove())),0===t.length)return;const i=()=>{addTextTracks(e,t,(t=>this.addedTracks.push(t)))};e.readyState>=e.HAVE_CURRENT_DATA?i():this.eventListeners.scope(e,"@@setTextTracks").once("loadeddata",i)}getActiveTextTrack(){return this.textTrack}setActiveTextTrack(t){const e=this.getMediaElement();this.nextTextTrack=t&&this.textTrackList.find((e=>e.language===t.language&&(!t.kind||e.kind===t.kind))),setActiveTextTrack(e,this.nextTextTrack,this.options.hideActiveTrack||isIOSFullscreen(e))}destroy(){this.detach(),this.removeAllListeners()}}!function(t){let e;!function(t){t.TextTrackListChanged="TrackListChanged",t.CurrentTextTrackChanged="CurrentTextTrackChanged",t.TextTrackCueChanged="TextTrackCueChanged"}(e=t.Events||(t.Events={}))}(TextTracksController||(TextTracksController={}));
@@ -1,8 +1,13 @@
1
+ declare global {
2
+ interface TextTrack {
3
+ customGroupId?: string | undefined;
4
+ }
5
+ }
1
6
  export type TextTrackItem = Readonly<PartialSome<RequiredStrict<Pick<HTMLTrackElement, 'src' | 'default'> & Pick<TextTrack, 'kind' | 'label' | 'language'>>, 'default' | 'kind'>>;
2
7
  export interface TextTrackInfo extends Pick<TextTrackItem, 'kind' | 'language' | 'label'> {
8
+ readonly id: string;
3
9
  }
4
10
  export type ActivateTextTrackInfo = OptionalToUndefined<PartialBut<Pick<TextTrackInfo, 'kind' | 'language'>, 'language'>>;
5
- export declare const DETACHED_GROUP_ID = "__detached__";
6
11
  /** Hack: MSE can't remove texttracks on detaching because of no browser api for that. */
7
12
  export declare function fakeDetachTextTracks(media: HTMLMediaElement): void;
8
13
  export declare function parseTextTracks(media: HTMLMediaElement): TextTrackInfo[];
@@ -10,3 +15,5 @@ export declare function isIOSFullscreen(media: HTMLMediaElement): boolean;
10
15
  export declare function setActiveTextTrack(media: HTMLMediaElement, tt: ActivateTextTrackInfo | undefined, hideActiveTrack: boolean): void;
11
16
  /** Dynamically add text tracks */
12
17
  export declare function addTextTracks(media: HTMLMediaElement, textTrackList: readonly TextTrackItem[], onAdd: (el: HTMLTrackElement) => void): void;
18
+ export declare function buildCueId(cue: Pick<TextTrackCue, 'startTime'>, index: number): string;
19
+ export declare function splitRows(text: string, preferLineLength: number): string[];
@@ -1 +1 @@
1
- import{isIOS}from"../../platform/isIOS";export const DETACHED_GROUP_ID="__detached__";export function fakeDetachTextTracks(e){[].forEach.call(e.textTracks,(e=>{const t=e;t.groupId&&(t.groupId="__detached__")}))}export function parseTextTracks(e){return 0===e.textTracks.length?[]:Array.from(e.textTracks).filter((e=>"__detached__"!==e.groupId&&!!e.language)).map((({kind:e,language:t,label:a})=>({kind:e,language:t,label:null!=a?a:""})))}export function isIOSFullscreen(e){return isIOS()&&!!e.webkitDisplayingFullscreen}export function setActiveTextTrack(e,t,a){if(0===e.textTracks.length)return;const{textTracks:n}=e;let r=!1;for(let e=0;e<n.length;e+=1){const l=n[e];if(r||l.language!==(null==t?void 0:t.language)||t.kind&&l.kind!==t.kind)l.mode="disabled";else{const e=a?"hidden":"showing";l.mode=e,r=!0}}}export function addTextTracks(e,t,a){const n=Array.from(e.textTracks).reduce(((e,t)=>(t.language&&(e[t.language]=t),e)),{});t.forEach((t=>{var r;if(!n[t.language]){const n=document.createElement("track");n.src=t.src,n.srclang=t.language,n.label=t.label,n.kind=null!==(r=t.kind)&&void 0!==r?r:"captions",n.default=!1,a(e.appendChild(n))}}))}
1
+ import{isIOS}from"../../platform/isIOS";const DETACHED_GROUP_ID="__detached__";export function fakeDetachTextTracks(t){[].forEach.call(t.textTracks,(t=>{const e=t;e.customGroupId&&(e.customGroupId="__detached__")}))}export function parseTextTracks(t){return 0===t.textTracks.length?[]:Array.prototype.filter.call(t.textTracks,(t=>"__detached__"!==t.customGroupId&&!!t.language)).map((({id:t,kind:e,language:n,label:l})=>({id:t,kind:e,language:n,label:null!=l?l:""})))}export function isIOSFullscreen(t){return isIOS()&&!!t.webkitDisplayingFullscreen}export function setActiveTextTrack(t,e,n){const{textTracks:l}=t;if(0===l.length)return;let r=!1;for(let t=0;t<l.length;t+=1){const a=l[t];if(r||a.language!==(null==e?void 0:e.language)||e.kind&&a.kind!==e.kind)a.mode="disabled";else{const t=n?"hidden":"showing";a.mode=t,r=!0}}}export function addTextTracks(t,e,n){const l=Array.prototype.reduce.call(t.textTracks,((t,e)=>(e.language&&(t[e.language]=e),t)),{});e.forEach((e=>{var r;if(!l[e.language]){const l=document.createElement("track");l.src=e.src,l.srclang=e.language,l.label=e.label,l.kind=null!==(r=e.kind)&&void 0!==r?r:"captions",l.default=!1,n(t.appendChild(l))}}))}export function buildCueId(t,e){return`${t.startTime}-${e}`}export function splitRows(t,e){var n;if(e<=0)return t.split(/\r?\n/);const l=[];let r=0,a=r,i="";for(let o=0;o<=t.length;o+=1){const c=t[o];i.length>0&&r<a&&(l[a]=null==c?i.trim():i.trimStart(),i=""),r=a;let s=null!==(n=l[r])&&void 0!==n?n:"";if("\n"===c||"\r\n"===c)i.length>0&&s.length+i.length<=e&&(s+=s.length>0?i:i.trimStart(),i=""),a+=1;else if(" "===c){if(i.length>0){s.length+i.length>e&&s.length>0?(a+=1,s=s.trimEnd()):(s+=0===s.length?i.trimStart():i,i="")}i+=c}else if(null==c&&i.length>0){s.length+i.length>e&&0!==s.length?(r+=1,s=i.trim()):s+=i}else c&&(i+=c);l[r]=s}return l}
@@ -11,8 +11,7 @@ declare global {
11
11
  removeEventListener<K extends keyof ManagedMediaSourceEventMap>(type: K, listener: (this: MediaSource, ev: ManagedMediaSourceEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
12
12
  }
13
13
  interface Window {
14
- WebKitMediaSource?: typeof MediaSource;
15
- ManagedMediaSource?: ManagedMediaSource;
14
+ ManagedMediaSource?: ManagedMediaSource & typeof MediaSource;
16
15
  }
17
16
  }
18
- export declare function getMediaSource(): typeof MediaSource | undefined;
17
+ export declare function getMediaSource(managedMediaSource?: 'prefer' | 'maybe' | undefined): typeof MediaSource | undefined;
@@ -1 +1 @@
1
- export function getMediaSource(){return window.MediaSource||window.WebKitMediaSource}
1
+ export function getMediaSource(e){return"prefer"===e&&window.ManagedMediaSource||"maybe"===e&&window.ManagedMediaSource&&!window.MediaSource?window.ManagedMediaSource:window.MediaSource}
@@ -0,0 +1,16 @@
1
+ export interface CueSegment {
2
+ readonly id: string;
3
+ readonly startTime: number | undefined;
4
+ readonly tag: keyof typeof TAG_NAME;
5
+ readonly node: HTMLElement;
6
+ }
7
+ declare const TAG_NAME: Record<string, string>;
8
+ export interface ParseCueTextOptions {
9
+ readonly preferLength?: number | undefined;
10
+ }
11
+ export interface ParseCueTextResult<P> {
12
+ readonly segments: P[][];
13
+ readonly rawText: string[];
14
+ }
15
+ export declare function parseCueText<P = CueSegment>(input0: string, map?: (segment: CueSegment, prevSegment: P | undefined) => P, { preferLength }?: ParseCueTextOptions): ParseCueTextResult<P>;
16
+ export {};
@@ -0,0 +1 @@
1
+ import{splitRows}from"./TextTracksController/utils";const ESCAPE={"&amp;":"&","&lt;":"<","&gt;":">","&lrm;":"‎","&rlm;":"‏","&nbsp;":" "},TAG_NAME={c:"span",i:"i",b:"b",u:"u",ruby:"ruby",rt:"rt",lang:"span"},TAG_ANNOTATION={v:"title",lang:"lang"},NEEDS_PARENT={rt:"ruby"};function computeSeconds(e,t,n,o){return 3600*(+e||0)+60*(+t||0)+(+n||0)+(+o||0)/1e3}function parseTimeStamp(e){const t=e.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);if(!t)return;const[,n,o,l,r]=t;return r?computeSeconds(n,o,l.replace(":",""),r):+n>59?computeSeconds(n,o,"",r):computeSeconds("",n,o,r)}function unescape(e){let t,n=e;for(;t=n.match(/&(amp|lt|gt|lrm|rlm|nbsp);/);)n=n.replace(t[0],(e=>ESCAPE[e]));return n}function nextToken(e){if(!e)return[e,void 0];const t=e.match(/^([^<]*)(<[^>]+>?)?/);if(!t)return[e,void 0];const n=t[1]?t[1]:t[2];return[e.substring(n.length),n]}function shouldAdd(e,t){return!NEEDS_PARENT[t.localName]||NEEDS_PARENT[t.localName]===e.localName}function createHtmlNode(e,t){const n=TAG_NAME[e];if(!n)return;const o=window.document.createElement(n),l=TAG_ANNOTATION[e];return l&&t&&(o[l]=t.trim()),o}export function parseCueText(e,t,{preferLength:n=0}={}){let o,l,r=e,s=-1;const a=[],c=[],u=[],i=e=>{var n,o;0===c.length&&c.push([]),c.at(-1).push(t?t(e,null===(o=null!==(n=c.at(-1))&&void 0!==n?n:c.at(-2))||void 0===o?void 0:o.at(-1)):e)},d=e=>{var t;const n=null!==(t=a.pop())&&void 0!==t?t:"",l=o;if(o=l.parentElement||void 0,null==o){i({id:e,startTime:s>=0?s:void 0,tag:n,node:l})}},p=(e,t)=>{t?u.push(e):u[u.length-1]+=e,t&&c.push([]),null==o?(e=>{const t=createHtmlNode("c","");t.appendChild(window.document.createTextNode(unescape(e)));const n={id:m(),node:t,tag:"c",startTime:s>=0?s:void 0};i(n)})(e):o.appendChild(window.document.createTextNode(unescape(e)))},m=()=>{var e,t;return`${c.length}-${(null!==(t=null===(e=c.at(-1))||void 0===e?void 0:e.length)&&void 0!==t?t:0)+1}`};for(;null!=([r,l]=nextToken(r),l);)if("<"===l[0])if("/"===l[1])a.at(-1)===l.substring(2).replace(">","")&&d(m());else{const e=parseTimeStamp(l.substring(1,l.length-1));if(e)s=e;else{const e=l.match(/^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/),t=e&&createHtmlNode(e[1],e[3]);!t||o&&!shouldAdd(o,t)||(e[2]&&(t.className=e[2].substring(1).replace("."," ")),a.push(e[1]),null==o||o.appendChild(t),o=t)}}else if(0===u.length||n>0&&u.at(-1).length+l.length>n){const e=splitRows(l,n);for(let t=0;t<e.length;t+=1)p(e[t],!0)}else p(l,!1);return{segments:c,rawText:u}}
package/onPageReady.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  export declare function isPageReady(): boolean;
2
- /** @returns cancel wait function */
3
- export declare function onPageReady(callback: VoidFunction): VoidFunction;
2
+ interface Options {
3
+ readonly timeout?: number | undefined;
4
+ }
5
+ /** @returns cancel wait function. */
6
+ export declare function onPageReady(callback: VoidFunction, options?: Options): VoidFunction;
7
+ export {};
package/onPageReady.js CHANGED
@@ -1 +1 @@
1
- export function isPageReady(){return"complete"===document.readyState}export function onPageReady(e){return isPageReady()?e():window.addEventListener("load",e,{once:!0}),()=>{window.removeEventListener("load",e)}}
1
+ import{delay}from"@js-toolkit/utils/delay";export function isPageReady(){return"complete"===document.readyState}export function onPageReady(e,t){let o;const n=()=>{window.removeEventListener("load",a),null==o||o.cancel()},a=()=>{n(),e()};return isPageReady()?e():(window.addEventListener("load",a,{once:!0}),(null==t?void 0:t.timeout)&&(o=delay(a,t.timeout))),n}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@js-toolkit/web-utils",
3
- "version": "1.52.0",
3
+ "version": "1.54.0",
4
4
  "description": "Web utils",
5
5
  "author": "VZH",
6
6
  "license": "MIT",
@@ -20,26 +20,32 @@
20
20
  "@js-toolkit/node-utils": "^1.2.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@js-toolkit/configs": "^3.90.0",
24
- "@js-toolkit/utils": "^1.51.1",
25
- "@types/uuid": "^9.0.8",
26
- "@typescript-eslint/eslint-plugin": "^6.21.0",
27
- "@typescript-eslint/parser": "^6.21.0",
23
+ "@eslint/compat": "^1.1.0",
24
+ "@eslint/eslintrc": "^3.1.0",
25
+ "@eslint/js": "^9.6.0",
26
+ "@js-toolkit/configs": "^3.93.0",
27
+ "@js-toolkit/utils": "^1.54.0",
28
+ "@types/eslint": "^8.56.10",
29
+ "@types/eslint__eslintrc": "^2.1.1",
30
+ "@types/eslint__js": "^8.42.3",
31
+ "@types/lodash.throttle": "^4.1.9",
32
+ "@types/uuid": "^10.0.0",
28
33
  "copyfiles": "^2.4.1",
29
- "eslint": "^8.56.0",
30
- "eslint-config-airbnb-base": "^15.0.0",
34
+ "eslint": "^9.6.0",
31
35
  "eslint-config-prettier": "^9.1.0",
32
36
  "eslint-plugin-import": "^2.29.1",
33
37
  "eslint-plugin-prettier": "^5.1.3",
34
38
  "eventemitter3": "^5.0.1",
35
- "prettier": "^3.2.5",
39
+ "lodash.throttle": "^4.1.1",
40
+ "prettier": "^3.3.2",
36
41
  "reconnecting-websocket": "^4.4.0",
37
- "rimraf": "^5.0.5",
38
- "terser": "^5.27.0",
39
- "typescript": "^5.3.3",
40
- "ua-parser-js": "^2.0.0-beta.2",
41
- "uuid": "^9.0.1",
42
- "webpack": "^5.90.1",
42
+ "rimraf": "^5.0.7",
43
+ "terser": "^5.31.1",
44
+ "typescript": "^5.5.2",
45
+ "typescript-eslint": "^7.14.1",
46
+ "ua-parser-js": "^2.0.0-beta.3",
47
+ "uuid": "^10.0.0",
48
+ "webpack": "^5.92.1",
43
49
  "yargs": "^17.7.2"
44
50
  }
45
51
  }
@@ -1,4 +1,3 @@
1
- /// <reference types="ua-parser-js/src/main/ua-parser" />
2
1
  type PlatformInfo = DeepReadonly<OmitStrict<UAParser.IResult, 'withClientHints' | 'withFeatureCheck'>> & {
3
2
  toStringObject(): Record<Keys<ExcludeKeysOfType<UAParser.IResult, AnyFunction>>, string>;
4
3
  };
@@ -1,7 +1,2 @@
1
- declare global {
2
- interface Window {
3
- WebKitSourceBuffer?: typeof SourceBuffer;
4
- }
5
- }
6
1
  /** Media Source Extensions */
7
2
  export declare function isMSESupported(): boolean;
@@ -1 +1 @@
1
- import{getMediaSource}from"../media/getMediaSource";function getSourceBuffer(){return window.SourceBuffer||window.WebKitSourceBuffer}export function isMSESupported(){var e;if(!(null===(e=getMediaSource())||void 0===e?void 0:e.isTypeSupported))return!1;const o=getSourceBuffer();return!o||o.prototype&&"function"==typeof o.prototype.appendBuffer&&"function"==typeof o.prototype.remove}
1
+ import{getMediaSource}from"../media/getMediaSource";function getSourceBuffer(){return window.SourceBuffer}export function isMSESupported(){var e;if(!(null===(e=getMediaSource())||void 0===e?void 0:e.isTypeSupported))return!1;const o=getSourceBuffer();return!o||o.prototype&&"function"==typeof o.prototype.appendBuffer&&"function"==typeof o.prototype.remove}
@@ -1 +1 @@
1
- function isTouchDevice(){return window.matchMedia("(any-pointer: coarse) and (any-hover: none)").matches}export function isMobileSimulation(){return isTouchDevice()&&1===navigator.maxTouchPoints}
1
+ import{isTouchSupported}from"./isTouchSupported";export function isMobileSimulation(){return isTouchSupported()&&1===navigator.maxTouchPoints}
@@ -0,0 +1 @@
1
+ export declare function isTouchSupported(): boolean;
@@ -0,0 +1 @@
1
+ export function isTouchSupported(){return window.matchMedia("(any-pointer: coarse) and (any-hover: none)").matches}
@@ -0,0 +1,38 @@
1
+ import { DataEventEmitter } from '@js-toolkit/utils/DataEventEmitter';
2
+ import { ErrorCompat } from '@js-toolkit/utils/ErrorCompat';
3
+ export declare class ServiceWorkerUnavailableError extends ErrorCompat {
4
+ constructor();
5
+ }
6
+ export declare class ServiceWorkerInstaller extends DataEventEmitter<{
7
+ registered: [{
8
+ registration: ServiceWorkerRegistration;
9
+ }];
10
+ unregistered: [{
11
+ registration: ServiceWorkerRegistration;
12
+ }];
13
+ updatePending: [{
14
+ registration: ServiceWorkerRegistration;
15
+ }];
16
+ updated: [{
17
+ registration: ServiceWorkerRegistration;
18
+ }];
19
+ error: [{
20
+ error: unknown;
21
+ }];
22
+ }, ServiceWorkerInstaller> {
23
+ static isAvailable(): boolean;
24
+ private registration;
25
+ private cancelDefferedRegister;
26
+ constructor();
27
+ destroy(): void;
28
+ register(swUrl: string | URL, options?: ServiceWorkerInstaller.RegistrationOptions | undefined): void;
29
+ unregister(): void;
30
+ }
31
+ export declare namespace ServiceWorkerInstaller {
32
+ interface Options {
33
+ }
34
+ interface RegistrationOptions extends globalThis.RegistrationOptions {
35
+ /** Wait for page ready. */
36
+ readonly deffered?: boolean | number;
37
+ }
38
+ }
@@ -0,0 +1 @@
1
+ import{__awaiter,__rest}from"tslib";import{DataEventEmitter}from"@js-toolkit/utils/DataEventEmitter";import{ErrorCompat}from"@js-toolkit/utils/ErrorCompat";import{onPageReady}from"../onPageReady";import{isLocalhost}from"./utils";export class ServiceWorkerUnavailableError extends ErrorCompat{constructor(){super(ServiceWorkerUnavailableError,"ServiceWorker is not available",{name:"ServiceWorkerUnavailableError"})}}export class ServiceWorkerInstaller extends DataEventEmitter{static isAvailable(){return"serviceWorker"in navigator}constructor(){super()}destroy(){this.cancelDefferedRegister&&this.cancelDefferedRegister(),this.removeAllListeners()}register(r,e){if(!ServiceWorkerInstaller.isAvailable())throw new ServiceWorkerUnavailableError;const t=null!=e?e:{},{deffered:i}=t,o=__rest(t,["deffered"]),s=()=>__awaiter(this,void 0,void 0,(function*(){try{if(isLocalhost()){const e=yield fetch(r);if(!e.ok)throw new Error(`No service worker found at '${e.url}'.`,{cause:`Response: ${e.status} ${e.statusText}`})}const e=yield navigator.serviceWorker.register(r,o);this.registration=e,this.emit("registered",{registration:e}),e.onupdatefound=()=>{const r=e.installing;null!=r&&(r.onstatechange=()=>{"installed"===r.state&&(navigator.serviceWorker.controller?this.emit("updatePending",{registration:e}):this.emit("updated",{registration:e}))},r.onerror=r=>{const e=new Error("Error during service worker installation",{cause:r});this.emit("error",{error:e})})}}catch(r){const e=new Error("Error during service worker registration",{cause:r});this.emit("error",{error:e})}}));i?this.cancelDefferedRegister=onPageReady(s,"number"==typeof i?{timeout:i}:void 0):s()}unregister(){var r;this.cancelDefferedRegister&&this.cancelDefferedRegister(),null===(r=this.registration)||void 0===r||r.unregister().catch((r=>{const e=new Error("Error during service worker unregister",{cause:r});this.emit("error",{error:e})}))}}
@@ -0,0 +1,14 @@
1
+ export declare function isLocalhost(hostname?: string): boolean;
2
+ /** Delete all caches that aren't named in `caches`. */
3
+ export declare function removeUnknownCaches<T extends Record<string, string>>(expectedCaches: T): Promise<unknown>;
4
+ export declare function addResourcesToCache(cacheName: string, resources: readonly RequestInfo[]): Promise<void>;
5
+ interface CacheFirstOptions extends Pick<FetchEvent, 'request'> {
6
+ readonly fallbackUrl?: string | undefined;
7
+ readonly logger?: Pick<Console, 'error' | 'debug'> | undefined;
8
+ readonly saveToCache?: ((options: {
9
+ request: Request;
10
+ response: Response;
11
+ }) => boolean) | undefined;
12
+ }
13
+ export declare function cacheFirst(cacheName: string, { request, fallbackUrl, saveToCache, logger }: CacheFirstOptions): Promise<Response>;
14
+ export {};
@@ -0,0 +1 @@
1
+ import{__awaiter}from"tslib";import{getErrorMessage}from"@js-toolkit/utils/getErrorMessage";export function isLocalhost(e=window.location.hostname){return!("localhost"!==e&&"[::1]"!==e&&!e.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/))}export function removeUnknownCaches(e){return caches.keys().then((r=>{const o=new Set(Object.values(e));return Promise.all(r.map((e=>{if(!o.has(e))return caches.delete(e)})))}))}export function addResourcesToCache(e,r){return __awaiter(this,void 0,void 0,(function*(){const o=yield caches.open(e);yield o.addAll(r)}))}export function cacheFirst(e,r){return __awaiter(this,arguments,void 0,(function*(e,{request:r,fallbackUrl:o,saveToCache:t,logger:n=console}){const s=yield caches.open(e),c=yield s.match(r);if(c)return n.debug("Found response in cache:",c),c;n.debug("No response for %s found in cache. About to fetch from network...",r.url);try{const e=yield fetch(r.clone());return n.debug("Response for %s from network is: %O",r.url,e),e.status<400&&(!t||t({request:r,response:e}))?(n.debug("Caching the response to",r.url),s.put(r,e.clone()).catch((e=>{n.error(getErrorMessage(new Error(`Caching error of ${r.url}`,{cause:e})))}))):n.debug("Not caching the response to",r.url),e}catch(e){const r=o&&(yield s.match(o));if(r)return r;throw n.error("Error in fetch handler:",e),e}}))}
@@ -0,0 +1,12 @@
1
+ export interface ViewableTrackerOptions {
2
+ /** Visibility part to detect state as viewable. */
3
+ readonly visiblePart?: boolean | number | undefined;
4
+ readonly scrollThrottle?: number | undefined;
5
+ readonly documentVisibility?: boolean | undefined;
6
+ readonly onChange: (viewable: boolean) => void;
7
+ }
8
+ export interface ViewableTracker {
9
+ readonly check: VoidFunction;
10
+ readonly destroy: VoidFunction;
11
+ }
12
+ export declare function getViewableTracker(element: HTMLElement, { visiblePart: visiblePartOption, scrollThrottle, documentVisibility, onChange, }: ViewableTrackerOptions): ViewableTracker;
@@ -0,0 +1 @@
1
+ import throttleFn from"lodash.throttle";export function getViewableTracker(t,{visiblePart:e=.8,scrollThrottle:i=200,documentVisibility:n=!0,onChange:o}){const r=+e;let s,l=0,c="visible"===document.visibilityState;const a=()=>{if(n&&"visible"!==document.visibilityState)return;const{top:e,bottom:i,height:l}=t.getBoundingClientRect(),c=l*r,a=e+c,d=window.innerHeight>=a&&i>c;s!==d&&(s=d,o(d))},d=()=>{const t="visible"===document.visibilityState;c!==t&&(c=t,t&&r>0?(s=void 0,a()):(r<=0||s!==t)&&o(t))},v=i&&i>0?throttleFn(a,i):()=>{n&&"visible"!==document.visibilityState||(l=window.requestAnimationFrame(a))};r>0&&window.addEventListener("scroll",v,{capture:!1,passive:!0}),n&&document.addEventListener("visibilitychange",d);return{check:()=>{r>0&&a(),n&&d()},destroy:()=>{cancelAnimationFrame(l),window.removeEventListener("scroll",v,{capture:!1}),document.removeEventListener("visibilitychange",d)}}}
@@ -1,8 +0,0 @@
1
- export interface Config {
2
- onUpdate?(registration: ServiceWorkerRegistration): void;
3
- onSuccess?(registration: ServiceWorkerRegistration): void;
4
- }
5
- export declare const UpdateEventType = "SW:UPDATE";
6
- export declare function register(config?: Config | undefined): void;
7
- export declare function unregister(): void;
8
- export declare function addUpdateListener(callback: VoidFunction): void;
package/serviceWorker.js DELETED
@@ -1 +0,0 @@
1
- import appEnv from"@js-toolkit/configs/appEnv";import buildConfig from"@js-toolkit/configs/buildConfig";const logPrefix="SW:";export const UpdateEventType="SW:UPDATE";const isLocalhost=Boolean("localhost"===window.location.hostname||"[::1]"===window.location.hostname||new RegExp(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/).exec(window.location.hostname));function registerValidSW(e,o){navigator.serviceWorker.register(e).then((e=>{e.onupdatefound=()=>{const r=e.installing;null!=r&&(r.onstatechange=()=>{"installed"===r.state&&(navigator.serviceWorker.controller?(console.log(logPrefix,"New content is available and will be used when all tabs for this page are closed. See https://cra.link/PWA."),window.dispatchEvent(new Event(UpdateEventType)),o&&o.onUpdate&&o.onUpdate(e)):(console.log(logPrefix,"Content is cached for offline use."),o&&o.onSuccess&&o.onSuccess(e)))},r.onerror=e=>{console.error(logPrefix,"Installing worker:",e)})}})).catch((e=>{console.error(logPrefix,"Error during service worker registration:",e.message||e)}))}function checkValidServiceWorker(e,o){return fetch(e,{headers:{"Service-Worker":"script"}}).then((r=>{const n=r.headers.get("content-type");404===r.status||null!=n&&!n.includes("javascript")?navigator.serviceWorker.ready.then((e=>{e.unregister().then((()=>{window.location.reload()}))})):registerValidSW(e,o)})).catch((()=>{console.log(logPrefix,"No internet connection found. App is running in offline mode.")}))}export function register(e){if(appEnv.prod&&"serviceWorker"in navigator){const o=buildConfig.client||buildConfig.default.client;if(new URL(o.output.publicPath,window.location.href).origin!==window.location.origin)return void console.warn(logPrefix,"Origins are different.");window.addEventListener("load",(()=>{const r=`${o.output.publicPath}${o.output.sw.swDest}`;isLocalhost?(checkValidServiceWorker(r,e),navigator.serviceWorker.ready.then((()=>{console.log(logPrefix,"This web app is being served cache-first by a service worker. To learn more, visit https://bit.ly/CRA-PWA")}))):registerValidSW(r,e)}))}}export function unregister(){"serviceWorker"in navigator&&navigator.serviceWorker.ready.then((e=>e.unregister())).catch((e=>{console.error(logPrefix,"Error during service worker unregister:",e.message||e)}))}export function addUpdateListener(e){window.addEventListener(UpdateEventType,e)}