@js-toolkit/web-utils 1.62.2 → 1.63.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/media/PipController.d.ts +5 -1
- package/media/PipController.js +1 -1
- package/media/TextTracksController/TextTracksController.d.ts +11 -2
- package/media/TextTracksController/TextTracksController.js +1 -1
- package/media/TextTracksController/utils.d.ts +3 -3
- package/media/TextTracksController/utils.js +1 -1
- package/media/resetMedia.js +1 -1
- package/media/toggleNativeSubtitles.d.ts +0 -5
- package/package.json +1 -1
package/media/PipController.d.ts
CHANGED
|
@@ -8,12 +8,13 @@ declare global {
|
|
|
8
8
|
}
|
|
9
9
|
}
|
|
10
10
|
export declare class PipController extends EventEmitter<PipController.EventMap> implements AsyncDisposable {
|
|
11
|
+
private readonly options;
|
|
11
12
|
private static isApiEnabled;
|
|
12
13
|
private static isWebkitApiEnabled;
|
|
13
14
|
static isAvailable(video: HTMLVideoElement): boolean;
|
|
14
15
|
get Events(): typeof PipController.Events;
|
|
15
16
|
private readonly listener;
|
|
16
|
-
constructor(video: HTMLVideoElement);
|
|
17
|
+
constructor(video: HTMLVideoElement, options?: PipController.Options);
|
|
17
18
|
isPip(): boolean;
|
|
18
19
|
getCurrentElement(): HTMLVideoElement | null;
|
|
19
20
|
request(): Promise<void>;
|
|
@@ -22,6 +23,9 @@ export declare class PipController extends EventEmitter<PipController.EventMap>
|
|
|
22
23
|
[Symbol.asyncDispose](): PromiseLike<void>;
|
|
23
24
|
}
|
|
24
25
|
export declare namespace PipController {
|
|
26
|
+
interface Options {
|
|
27
|
+
readonly toggleNativeSubtitles?: boolean | undefined;
|
|
28
|
+
}
|
|
25
29
|
enum Events {
|
|
26
30
|
Change = "change"
|
|
27
31
|
}
|
package/media/PipController.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{EventEmitterListener}from"../EventEmitterListener";const getPipUnavailableError=()=>new Error("PiP is not available");export class PipController extends EventEmitter{static isApiEnabled(){return!!HTMLVideoElement.prototype.requestPictureInPicture&&!!document.exitPictureInPicture&&!!document.pictureInPictureEnabled}static isWebkitApiEnabled(e){return!!e.webkitSupportsPresentationMode&&e.webkitSupportsPresentationMode("picture-in-picture")}static isAvailable(e){return this.isApiEnabled()&&!e.disablePictureInPicture||this.isWebkitApiEnabled(e)}get Events(){return PipController.Events}listener;constructor(e){if(super(),this.listener=new EventEmitterListener(e),PipController.isAvailable(e)){const e=()=>{this.emit(this.Events.Change,{pip:!0})},t=()=>{this.emit(this.Events.Change,{pip:!1})};PipController.isApiEnabled()?(this.listener.on("enterpictureinpicture",e),this.listener.on("leavepictureinpicture",t)):this.listener.on("webkitpresentationmodechanged",(()=>{let i=this.listener.target.webkitPresentationMode;return()=>{"picture-in-picture"===this.listener.target.webkitPresentationMode?e():"picture-in-picture"===i&&t(),i=this.listener.target.webkitPresentationMode}})(),!0)}}isPip(){return PipController.isApiEnabled()?document.pictureInPictureElement===this.listener.target:"picture-in-picture"===this.listener.target.webkitPresentationMode}getCurrentElement(){return this.isPip()?this.listener.target:null}request(){return new Promise(((e,t)=>{if(this.isPip())e();else{if(!PipController.isAvailable(this.listener.target))throw getPipUnavailableError();PipController.isApiEnabled()?this.listener.target.requestPictureInPicture().then((()=>e()),t):(this.listener.once("webkitpresentationmodechanged",(()=>{"picture-in-picture"===this.listener.target.webkitPresentationMode?e():t(new Error("Something went wrong."))}),!0),this.listener.target.webkitSetPresentationMode("picture-in-picture"))}}))}exit(){return new Promise(((e,t)=>{if(this.isPip()){if(!PipController.isAvailable(this.listener.target))throw getPipUnavailableError();PipController.isApiEnabled()?document.exitPictureInPicture().then(e,t):(this.listener.once("webkitpresentationmodechanged",(()=>{"picture-in-picture"!==this.listener.target.webkitPresentationMode?e():t(new Error("Something went wrong."))}),!0),this.listener.target.webkitSetPresentationMode("inline"))}else e()}))}destroy(){return this.exit().finally((()=>{this.removeAllListeners(),this.listener.removeAllListeners()}))}[Symbol.asyncDispose](){return this.destroy()}}!function(e){let t;!function(e){e.Change="change"}(t=e.Events||(e.Events={}))}(PipController||(PipController={}));
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{EventEmitterListener}from"../EventEmitterListener";import{toggleNativeSubtitles}from"./toggleNativeSubtitles";const getPipUnavailableError=()=>new Error("PiP is not available");export class PipController extends EventEmitter{options;static isApiEnabled(){return!!HTMLVideoElement.prototype.requestPictureInPicture&&!!document.exitPictureInPicture&&!!document.pictureInPictureEnabled}static isWebkitApiEnabled(e){return!!e.webkitSupportsPresentationMode&&e.webkitSupportsPresentationMode("picture-in-picture")}static isAvailable(e){return this.isApiEnabled()&&!e.disablePictureInPicture||this.isWebkitApiEnabled(e)}get Events(){return PipController.Events}listener;constructor(e,t={}){if(super(),this.options=t,this.listener=new EventEmitterListener(e),PipController.isAvailable(e)){const e=(()=>{const e=()=>{e.nativeSubtitles=this.options.toggleNativeSubtitles&&this.listener.target.textTracks.length>0,e.nativeSubtitles&&toggleNativeSubtitles(!0,this.listener.target.textTracks),this.emit(this.Events.Change,{pip:!0})};return e.nativeSubtitles=void 0,e})(),t=()=>{e.nativeSubtitles&&toggleNativeSubtitles(!1,this.listener.target.textTracks),this.emit(this.Events.Change,{pip:!1})};PipController.isApiEnabled()?(this.listener.on("enterpictureinpicture",e),this.listener.on("leavepictureinpicture",t)):this.listener.on("webkitpresentationmodechanged",(()=>{let i=this.listener.target.webkitPresentationMode;return()=>{"picture-in-picture"===this.listener.target.webkitPresentationMode?e():"picture-in-picture"===i&&t(),i=this.listener.target.webkitPresentationMode}})(),!0)}}isPip(){return PipController.isApiEnabled()?document.pictureInPictureElement===this.listener.target:"picture-in-picture"===this.listener.target.webkitPresentationMode}getCurrentElement(){return this.isPip()?this.listener.target:null}request(){return new Promise(((e,t)=>{if(this.isPip())e();else{if(!PipController.isAvailable(this.listener.target))throw getPipUnavailableError();PipController.isApiEnabled()?this.listener.target.requestPictureInPicture().then((()=>e()),t):(this.listener.once("webkitpresentationmodechanged",(()=>{"picture-in-picture"===this.listener.target.webkitPresentationMode?e():t(new Error("Something went wrong."))}),!0),this.listener.target.webkitSetPresentationMode("picture-in-picture"))}}))}exit(){return new Promise(((e,t)=>{if(this.isPip()){if(!PipController.isAvailable(this.listener.target))throw getPipUnavailableError();PipController.isApiEnabled()?document.exitPictureInPicture().then(e,t):(this.listener.once("webkitpresentationmodechanged",(()=>{"picture-in-picture"!==this.listener.target.webkitPresentationMode?e():t(new Error("Something went wrong."))}),!0),this.listener.target.webkitSetPresentationMode("inline"))}else e()}))}destroy(){return this.exit().finally((()=>{this.removeAllListeners(),this.listener.removeAllListeners()}))}[Symbol.asyncDispose](){return this.destroy()}}!function(e){let t;!function(e){e.Change="change"}(t=e.Events||(e.Events={}))}(PipController||(PipController={}));
|
|
@@ -3,6 +3,12 @@ import { type ActivateTextTrackInfo, type TextTrackInfo, type TextTrackItem } fr
|
|
|
3
3
|
declare global {
|
|
4
4
|
interface HTMLMediaElementEventMap extends TextTracksEventMap {
|
|
5
5
|
}
|
|
6
|
+
interface TextTrack {
|
|
7
|
+
native?: boolean | undefined;
|
|
8
|
+
}
|
|
9
|
+
interface TextTrackList {
|
|
10
|
+
[index: number]: TextTrack | undefined;
|
|
11
|
+
}
|
|
6
12
|
}
|
|
7
13
|
interface TextTracksEventMap {
|
|
8
14
|
texttracklistchange: CustomEvent<TextTracksController.EventMap[TextTracksController.Events.TextTrackListChanged][0]>;
|
|
@@ -13,13 +19,16 @@ export interface Cue extends PartialBut<OmitStrict<VTTCue, keyof EventTarget | '
|
|
|
13
19
|
readonly rows: string[];
|
|
14
20
|
}
|
|
15
21
|
export type { TextTrackInfo, ActivateTextTrackInfo, TextTrackItem };
|
|
22
|
+
export type ActiveTextTrackInfo = TextTrackInfo & Pick<TextTrack, 'mode'>;
|
|
16
23
|
export declare class TextTracksController extends EventEmitter<TextTracksController.EventMap> implements Disposable {
|
|
17
24
|
private readonly options;
|
|
18
25
|
private readonly eventListeners;
|
|
19
26
|
private addedTracks;
|
|
20
27
|
private textTrackList;
|
|
21
28
|
private media;
|
|
29
|
+
/** Already set. */
|
|
22
30
|
private textTrack;
|
|
31
|
+
/** To be set. */
|
|
23
32
|
private nextTextTrack;
|
|
24
33
|
get Events(): typeof TextTracksController.Events;
|
|
25
34
|
constructor(options?: TextTracksController.Options);
|
|
@@ -30,7 +39,7 @@ export declare class TextTracksController extends EventEmitter<TextTracksControl
|
|
|
30
39
|
attach(media: HTMLMediaElement): void;
|
|
31
40
|
getTextTracks(): readonly TextTrackInfo[];
|
|
32
41
|
setTextTracks(textTrackList: readonly TextTrackItem[]): void;
|
|
33
|
-
getActiveTextTrack():
|
|
42
|
+
getActiveTextTrack(): ActiveTextTrackInfo | undefined;
|
|
34
43
|
setActiveTextTrack(tt: ActivateTextTrackInfo | undefined): boolean;
|
|
35
44
|
destroy(): void;
|
|
36
45
|
[Symbol.dispose](): void;
|
|
@@ -57,7 +66,7 @@ export declare namespace TextTracksController {
|
|
|
57
66
|
];
|
|
58
67
|
[Events.CurrentTextTrackChanged]: [
|
|
59
68
|
{
|
|
60
|
-
readonly textTrack:
|
|
69
|
+
readonly textTrack: ActiveTextTrackInfo | undefined;
|
|
61
70
|
readonly index: number;
|
|
62
71
|
}
|
|
63
72
|
];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{EventListeners}from"../../EventListeners";import{MediaNotAttachedError}from"../MediaNotAttachedError";import{parseTextTracks,setActiveTextTrack,addTextTracks,isIOSFullscreen,splitRows,buildCueId}from"./utils";function dispatchNativeEvent(t,e,s){t.dispatchEvent(new CustomEvent(e,{detail:s}))}export class TextTracksController extends EventEmitter{options;eventListeners=new EventListeners;addedTracks=[];textTrackList=[];media;textTrack;nextTextTrack;get Events(){return TextTracksController.Events}constructor(t){super(),this.options={emitNativeEvents:!1,hideActiveTrack:!0,preferCueRowLength:0},t&&this.setOptions(t)}setOptions(t){Object.assign(this.options,{...t,emitNativeEvents:t.emitNativeEvents??this.options.emitNativeEvents,hideActiveTrack:t.hideActiveTrack??this.options.hideActiveTrack,preferCueRowLength:t.preferCueRowLength??this.options.preferCueRowLength})}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();const e=(()=>{const e=[];return s=>{const{activeCues:i}=s.target;if(!i)return;let a=e.length!==i.length;const r=new Array(i.length);for(let t=0;t<r.length;t+=1){const s=i[t];s.id=s.id||buildCueId(s,t),
|
|
1
|
+
import{EventEmitter}from"@js-toolkit/utils/EventEmitter";import{EventListeners}from"../../EventListeners";import{MediaNotAttachedError}from"../MediaNotAttachedError";import{parseTextTracks,setActiveTextTrack,addTextTracks,isIOSFullscreen,splitRows,buildCueId}from"./utils";function dispatchNativeEvent(t,e,s){t.dispatchEvent(new CustomEvent(e,{detail:s}))}export class TextTracksController extends EventEmitter{options;eventListeners=new EventListeners;addedTracks=[];textTrackList=[];media;textTrack;nextTextTrack;get Events(){return TextTracksController.Events}constructor(t){super(),this.options={emitNativeEvents:!1,hideActiveTrack:!0,preferCueRowLength:0},t&&this.setOptions(t)}setOptions(t){Object.assign(this.options,{...t,emitNativeEvents:t.emitNativeEvents??this.options.emitNativeEvents,hideActiveTrack:t.hideActiveTrack??this.options.hideActiveTrack,preferCueRowLength:t.preferCueRowLength??this.options.preferCueRowLength})}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();const e=(()=>{const e=[];return s=>{const{activeCues:i}=s.target;if(!i)return;let a=e.length!==i.length;const r=new Array(i.length);for(let t=0;t<r.length;t+=1){const s=i[t];s.id=s.id||buildCueId(s,t),s.rows=splitRows(s.text,this.options.preferCueRowLength),r[t]=s,a||e[t]?.id===s.id||(a=!0),e[t]=s}e.length>r.length&&e.splice(r.length-e.length),a&&(this.emit(this.Events.TextTrackCueChanged,{textTrack:s.target,cues:r}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackcuechange",{textTrack:s.target,cues:r}))}})(),s=()=>{this.textTrackList=parseTextTracks(t),this.emit(this.Events.TextTrackListChanged,{textTracks:this.textTrackList}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttracklistchange",{textTracks:this.textTrackList}),setActiveTextTrack(t,this.textTrack??this.nextTextTrack,this.options.hideActiveTrack&&!isIOSFullscreen(t))};this.media=t;let i=!1;const a=t=>{i||s(),this.eventListeners.scope(t).on("cuechange",e)},r=t=>{s(),this.eventListeners.scope(t).off("cuechange",e)};if(this.eventListeners.scope(this.media.textTracks).on("change",(()=>{const{textTracks:e}=t,{nextTextTrack:s}=this;let i=-1;for(let a=0;a<e.length;a+=1){const r=e[a];if(r.language===s?.language&&r.kind===s?.kind||isIOSFullscreen(t)||(r.mode="disabled"),"disabled"!==r.mode&&i>=0&&(r.mode="disabled"),"disabled"!==r.mode){if(i=a,!r.native&&!isIOSFullscreen(t)){const t=this.options.hideActiveTrack?"hidden":"showing";r.mode!==t&&(r.mode=t)}r.native&&r.language===s?.language&&r.kind===s.kind&&(r.mode="showing")}}const a=e[i]&&((this.textTrackList[i]&&{...this.textTrackList[i],mode:e[i].mode})??{id:e[i].id,kind:e[i].kind,language:e[i].language,label:e[i].label,mode:e[i].mode});this.textTrack?.language===a?.language&&this.textTrack?.kind===a?.kind&&this.textTrack?.mode===a?.mode||(this.textTrack=a,this.nextTextTrack=this.textTrack,this.emit(this.Events.CurrentTextTrackChanged,{textTrack:this.textTrack,index:i}),this.options.emitNativeEvents&&dispatchNativeEvent(t,"texttrackchange",{textTrack:this.textTrack,index:i}))})).on("addtrack",(({track:t})=>t&&a(t))).on("removetrack",(({track:t})=>t&&r(t))),this.media.textTracks.length>0){this.textTrackList=parseTextTracks(t),i=!0;try{Array.prototype.forEach.call(this.media.textTracks,a),s()}finally{i=!1}}}getTextTracks(){return this.textTrackList}setTextTracks(t){const e=this.getMediaElement();if(this.eventListeners.scope(e,"@@setTextTracks").removeAllListeners(),this.addedTracks.forEach((t=>t.remove())),0===t.length)return;const s=()=>{addTextTracks(e,t,(t=>this.addedTracks.push(t))),this.textTrackList=parseTextTracks(e)};e.readyState>=e.HAVE_CURRENT_DATA?s():this.eventListeners.scope(e,"@@setTextTracks").once("loadeddata",s)}getActiveTextTrack(){return this.textTrack}setActiveTextTrack(t){const e=this.getMediaElement();return 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()}[Symbol.dispose](){this.destroy()}}!function(t){let e;!function(t){t.TextTrackListChanged="TrackListChanged",t.CurrentTextTrackChanged="CurrentTextTrackChanged",t.TextTrackCueChanged="TextTrackCueChanged"}(e=t.Events||(t.Events={}))}(TextTracksController||(TextTracksController={}));
|
|
@@ -3,15 +3,15 @@ declare global {
|
|
|
3
3
|
customGroupId?: string | undefined;
|
|
4
4
|
}
|
|
5
5
|
}
|
|
6
|
-
export type TextTrackItem = Readonly<PartialSome<RequiredStrict<Pick<HTMLTrackElement, 'src'> & Pick<TextTrack, 'kind' | 'label' | 'language'>>, 'kind'
|
|
7
|
-
export interface TextTrackInfo extends Pick<TextTrackItem, 'kind' | 'language' | 'label'> {
|
|
8
|
-
readonly id: string;
|
|
6
|
+
export type TextTrackItem = Readonly<OptionalToUndefined<PartialSome<RequiredStrict<Pick<HTMLTrackElement, 'src'> & Pick<TextTrack, 'kind' | 'label' | 'language'>>, 'kind'>>>;
|
|
7
|
+
export interface TextTrackInfo extends Pick<TextTrackItem, 'kind' | 'language' | 'label'>, Pick<TextTrack, 'id'> {
|
|
9
8
|
}
|
|
10
9
|
export type ActivateTextTrackInfo = OptionalToUndefined<PartialBut<Pick<TextTrackInfo, 'kind' | 'language'>, 'language'>>;
|
|
11
10
|
/** Hack: MSE can't remove texttracks on detaching because of no browser api for that. */
|
|
12
11
|
export declare function fakeDetachTextTracks(media: HTMLMediaElement): void;
|
|
13
12
|
export declare function parseTextTracks(media: HTMLMediaElement): TextTrackInfo[];
|
|
14
13
|
export declare function isIOSFullscreen(media: HTMLMediaElement): boolean;
|
|
14
|
+
export declare function findTextTrack(media: Pick<HTMLMediaElement, 'textTracks'>, conditions: OptionalToUndefined<Partial<Pick<TextTrack, 'mode' | 'language'>>>[]): TextTrack | undefined;
|
|
15
15
|
export declare function setActiveTextTrack(media: HTMLMediaElement, tt: ActivateTextTrackInfo | undefined, hideActiveTrack: boolean): boolean;
|
|
16
16
|
/** Dynamically add text tracks if they not exist. */
|
|
17
17
|
export declare function addTextTracks(media: HTMLMediaElement, textTrackList: readonly TextTrackItem[], onAdd: (el: HTMLTrackElement) => void): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{isIOS}from"../../platform/isIOS";const DETACHED_GROUP_ID="__detached__";export function fakeDetachTextTracks(
|
|
1
|
+
import{isIOS}from"../../platform/isIOS";const DETACHED_GROUP_ID="__detached__";export function fakeDetachTextTracks(e){Array.prototype.forEach.call(e.textTracks,(e=>{const t=e;t.customGroupId&&(t.customGroupId="__detached__")}))}export function parseTextTracks(e){return 0===e.textTracks.length?[]:Array.prototype.reduce.call(e.textTracks,((e,{customGroupId:t,id:n,kind:r,language:l,label:a})=>("__detached__"!==t&&l&&e.push({id:n,kind:r,language:l,label:a??""}),e)),[])}export function isIOSFullscreen(e){return isIOS()&&!!e.webkitDisplayingFullscreen}export function findTextTrack(e,t){return Array.prototype.find.call(e.textTracks,(({mode:e,language:n})=>t.some((t=>!(null!=t.mode&&t.mode!==e||null!=t.language&&t.language!==n)))))}export function setActiveTextTrack(e,t,n){const{textTracks:r}=e;if(0===r.length)return!1;let l=!1,a=!1;for(let e=0;e<r.length;e+=1){const o=r[e];if(a||o.language!==t?.language||t.kind&&o.kind!==t.kind)"disabled"!==o.mode&&(o.mode="disabled",l=!0);else{const e=n?"hidden":"showing";o.mode=e,a=!0,l=!0}}return l}export function addTextTracks(e,t,n){const r=Array.prototype.reduce.call(e.textTracks,((e,t)=>(t.language&&(e[t.language]=t),e)),{});t.forEach((t=>{if(!r[t.language]){const r=document.createElement("track");r.src=t.src,r.srclang=t.language,r.label=t.label,r.kind=t.kind??"captions",r.default=!1,n(e.appendChild(r))}}))}export function buildCueId(e,t){return`${e.startTime}-${t}`}export function splitRows(e,t){if(t<=0)return e.split(/\r?\n/);const n=[];let r=0,l=r,a="";for(let o=0;o<=e.length;o+=1){const c=e[o];a.length>0&&r<l&&(n[l]=null==c?a.trim():a.trimStart(),a=""),r=l;let i=n[r]??"";if("\n"===c||"\r\n"===c)a.length>0&&i.length+a.length<=t&&(i+=i.length>0?a:a.trimStart(),a=""),l+=1;else if(" "===c){if(a.length>0){i.length+a.length>t&&i.length>0?(l+=1,i=i.trimEnd()):(i+=0===i.length?a.trimStart():a,a="")}a+=c}else if(null==c&&a.length>0){i.length+a.length>t&&0!==i.length?(r+=1,i=a.trim()):i+=a}else c&&(a+=c);n[r]=i}return n}
|
package/media/resetMedia.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export function resetMedia(e){
|
|
1
|
+
export function resetMedia(e){(e.src||e.srcObject||e.childElementCount>0)&&(URL.revokeObjectURL(e.src),e.removeAttribute("src"),e.srcObject=null,e.childElementCount>0&&Array.prototype.filter.call(e.children,(e=>e instanceof HTMLSourceElement)).forEach((r=>e.removeChild(r))),e.load())}
|