@vime/core
Version:
Customizable, extensible, accessible and framework agnostic media player.
690 lines (684 loc) • 25.6 kB
JavaScript
import { r as registerInstance, c as createEvent, h, g as getElement } from './index-f5fd0f81.js';
import { p as noop, D as Disposal, q as isFunction, n as isNil, l as listen, c as withComponentRegistry, w as watchComponentRegistry, x as isNull, e as isUndefined, u as isNumber, h as isString } from './withComponentRegistry-28311671.js';
import { d as IS_IOS, e as canUsePiPInChrome, f as canUsePiPInSafari, g as canUsePiP } from './support-b6811262.js';
import { L as LazyLoader } from './LazyLoader-295ab4d4.js';
import { M as MediaType } from './MediaType-aec4c150.js';
import { V as ViewType } from './ViewType-6da43616.js';
import { w as withProviderConnect, c as createProviderDispatcher } from './ProviderConnect-42dc4f0d.js';
import { w as withProviderContext } from './withProviderContext-a9e7f1bc.js';
import { a as audioRegex, v as videoRegex, h as hlsRegex } from './utils-7dc44688.js';
import { F as FullscreenController, m as mitt } from './FullscreenController-b811bf0c.js';
import './Provider-2e7e8366.js';
import './PlayerProps-2c57fcea.js';
import './withPlayerContext-4c52f564.js';
import './PlayerEvents-5c5704d6.js';
var __awaiter$2 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* Extends the base `FullscreenController` with additional logic for handling fullscreen
* on iOS Safari where the native Fullscreen API is not available (in this case it fallsback to
* using the `VideoPresentationController`).
*/
class VideoFullscreenController extends FullscreenController {
constructor(host, presentationController) {
super(host);
this.host = host;
this.presentationController = presentationController;
}
get isFullscreen() {
return this.presentationController.isFullscreenMode;
}
/**
* Whether a fallback fullscreen API is available on Safari using presentation modes. This
* is only used on iOS where the native fullscreen API is not available.
*
* @link https://developer.apple.com/documentation/webkitjs/htmlvideoelement/1631913-webkitpresentationmode
*/
get isSupported() {
return this.presentationController.isSupported;
}
makeEnterFullscreenRequest() {
return __awaiter$2(this, void 0, void 0, function* () {
return this.presentationController.setPresentationMode('fullscreen');
});
}
makeExitFullscreenRequest() {
return __awaiter$2(this, void 0, void 0, function* () {
return this.presentationController.setPresentationMode('inline');
});
}
addFullscreenChangeEventListener() {
if (!this.isSupported)
return noop;
this.presentationController.on('change', this.handlePresentationModeChange.bind(this));
return () => {
this.presentationController.off('change', this.handlePresentationModeChange.bind(this));
};
}
handlePresentationModeChange() {
this.handleFullscreenChange();
}
addFullscreenErrorEventListener() {
return noop;
}
}
var __awaiter$1 = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
/**
* Contains the logic for handling presentation modes on Safari. This class is used by
* the `VideoFullscreenController` as a fallback when the native Fullscreen API is not
* available (ie: iOS Safari).
*/
class VideoPresentationController {
constructor(host) {
this.host = host;
this.disposal = new Disposal();
this.emitter = mitt();
const disconnectedCallback = host.disconnectedCallback;
host.disconnectedCallback = () => __awaiter$1(this, void 0, void 0, function* () {
yield this.destroy();
disconnectedCallback === null || disconnectedCallback === void 0 ? void 0 : disconnectedCallback.call(host);
});
}
get videoElement() {
var _a;
if (((_a = this.host.mediaEl) === null || _a === void 0 ? void 0 : _a.tagName.toLowerCase()) === 'video') {
return this.host.mediaEl;
}
return undefined;
}
/**
* The current presentation mode, possible values include `inline`, `picture-in-picture` and
* `fullscreen`. Only available in Safari.
*
* @default undefined
* @link https://developer.apple.com/documentation/webkitjs/htmlvideoelement/1631913-webkitpresentationmode
*/
get presentationMode() {
var _a;
return (_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.webkitPresentationMode;
}
/**
* Whether the current `presentationMode` is `inline`.
*/
get isInlineMode() {
return this.presentationMode === 'inline';
}
/**
* Whether the current `presentationMode` is `picture-in-picture`.
*/
get isPictureInPictureMode() {
return this.presentationMode === 'inline';
}
/**
* Whether the current `presentationMode` is `fullscreen`.
*/
get isFullscreenMode() {
return this.presentationMode === 'fullscreen';
}
/**
* Whether the presentation mode API is available.
*
* @link https://developer.apple.com/documentation/webkitjs/htmlvideoelement/1628805-webkitsupportsfullscreen
*/
get isSupported() {
var _a, _b, _c;
return (IS_IOS &&
isFunction((_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.webkitSetPresentationMode) &&
((_c = (_b = this.videoElement) === null || _b === void 0 ? void 0 : _b.webkitSupportsFullscreen) !== null && _c !== void 0 ? _c : false));
}
setPresentationMode(mode) {
var _a, _b;
(_b = (_a = this.videoElement) === null || _a === void 0 ? void 0 : _a.webkitSetPresentationMode) === null || _b === void 0 ? void 0 : _b.call(_a, mode);
}
on(type, handler) {
// @ts-expect-error - not typed yet.
this.emitter.on(type, handler);
}
off(type, handler) {
// @ts-expect-error - not typed yet.
this.emitter.off(type, handler);
}
destroy() {
this.setPresentationMode('inline');
this.disposal.empty();
}
addPresentationModeChangeEventListener() {
if (!this.isSupported || isNil(this.videoElement))
return noop;
return listen(this.videoElement, 'webkitpresentationmodechanged', this.handlePresentationModeChange.bind(this));
}
handlePresentationModeChange() {
this.emitter.emit('change', this.presentationMode);
}
}
const fileCss = "audio.sc-vm-file,video.sc-vm-file{border-radius:inherit;vertical-align:middle;width:100%;outline:0}video.sc-vm-file{position:absolute;top:0;left:0;border:0;height:100%;user-select:none}";
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try {
step(generator.next(value));
}
catch (e) {
reject(e);
} }
function rejected(value) { try {
step(generator["throw"](value));
}
catch (e) {
reject(e);
} }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const File = class {
constructor(hostRef) {
registerInstance(this, hostRef);
this.vmLoadStart = createEvent(this, "vmLoadStart", 7);
this.vmError = createEvent(this, "vmError", 7);
this.vmMediaElChange = createEvent(this, "vmMediaElChange", 7);
this.vmSrcSetChange = createEvent(this, "vmSrcSetChange", 7);
this.textTracksDisposal = new Disposal();
this.wasPausedBeforeSeeking = true;
this.currentSrcSet = [];
this.mediaQueryDisposal = new Disposal();
/** @internal Whether an external SDK will attach itself to the media player and control it. */
this.willAttach = false;
/** @inheritdoc */
this.preload = 'metadata';
/**
* The playback rates that are available for this media.
*/
this.playbackRates = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2];
/** @internal */
this.language = 'en';
/** @internal */
this.autoplay = false;
/** @internal */
this.controls = false;
/** @internal */
this.loop = false;
/** @internal */
this.muted = false;
/** @internal */
this.playsinline = false;
/** @internal */
this.noConnect = false;
/** @internal */
this.paused = true;
/** @internal */
this.currentTime = 0;
/** @internal */
this.volume = 0;
/** @internal */
this.playbackReady = false;
/** @internal */
this.playbackStarted = false;
this.presentationController = new VideoPresentationController(this);
this.fullscreenController = new VideoFullscreenController(this, this.presentationController);
/** @internal */
this.currentTextTrack = -1;
/** @internal */
this.hasCustomTextManager = false;
/** @internal */
this.isTextTrackVisible = true;
/** @internal */
this.shouldRenderNativeTextTracks = true;
withComponentRegistry(this);
withProviderConnect(this);
withProviderContext(this, [
'playbackReady',
'playbackStarted',
'currentTime',
'volume',
'paused',
'currentTextTrack',
'isTextTrackVisible',
'shouldRenderNativeTextTracks',
]);
watchComponentRegistry(this, 'vm-poster', regs => {
[this.vmPoster] = regs;
});
}
onMediaTitleChange() {
this.dispatch('mediaTitle', this.mediaTitle);
}
onPosterChange() {
var _a;
if (!this.playbackStarted)
(_a = this.mediaEl) === null || _a === void 0 ? void 0 : _a.load();
}
onViewTypeChange() {
this.dispatch('viewType', this.viewType);
}
connectedCallback() {
this.initLazyLoader();
this.dispatch = createProviderDispatcher(this);
this.onViewTypeChange();
this.onPosterChange();
this.onMediaTitleChange();
this.addPresentationControllerListeners();
}
componentDidRender() {
if (this.prevMediaEl !== this.mediaEl) {
this.prevMediaEl = this.mediaEl;
this.vmMediaElChange.emit(this.mediaEl);
this.presentationController.addPresentationModeChangeEventListener();
}
}
componentDidLoad() {
this.onViewTypeChange();
}
disconnectedCallback() {
var _a;
this.mediaQueryDisposal.empty();
this.textTracksDisposal.empty();
this.cancelTimeUpdates();
(_a = this.lazyLoader) === null || _a === void 0 ? void 0 : _a.destroy();
this.wasPausedBeforeSeeking = true;
}
initLazyLoader() {
this.lazyLoader = new LazyLoader(this.host, ['data-src', 'data-poster'], () => {
if (isNil(this.mediaEl))
return;
const poster = this.mediaEl.getAttribute('data-poster');
if (!isNull(poster))
this.mediaEl.setAttribute('poster', poster);
this.refresh();
this.didSrcSetChange();
});
}
refresh() {
if (isNil(this.mediaEl))
return;
const { children } = this.mediaEl;
for (let i = 0; i <= children.length - 1; i += 1) {
const child = children[i];
const src = child.getAttribute('data-src') ||
child.getAttribute('src') ||
child.getAttribute('data-vs');
child.removeAttribute('src');
if (isNull(src))
continue;
child.setAttribute('data-vs', src);
child.setAttribute('src', src);
}
}
didSrcSetChange() {
if (isNil(this.mediaEl))
return;
const sources = Array.from(this.mediaEl.querySelectorAll('source'));
const srcSet = sources.map(source => {
var _a;
return ({
src: source.getAttribute('data-vs'),
media: (_a = source.getAttribute('data-media')) !== null && _a !== void 0 ? _a : undefined,
ref: source,
});
});
const didChange = this.currentSrcSet.length !== srcSet.length ||
srcSet.some((resource, i) => this.currentSrcSet[i].src !== resource.src);
if (didChange) {
this.currentSrcSet = srcSet;
this.onSrcSetChange();
}
}
onSrcSetChange() {
var _a;
this.textTracksDisposal.empty();
this.mediaQueryDisposal.empty();
this.vmLoadStart.emit();
this.vmSrcSetChange.emit(this.currentSrcSet);
if (!this.willAttach)
(_a = this.mediaEl) === null || _a === void 0 ? void 0 : _a.load();
}
hasCustomPoster() {
return !IS_IOS && !isUndefined(this.vmPoster);
}
cancelTimeUpdates() {
if (isNumber(this.timeRAF))
window.cancelAnimationFrame(this.timeRAF);
this.timeRAF = undefined;
}
requestTimeUpdates() {
var _a, _b;
this.dispatch('currentTime', (_b = (_a = this.mediaEl) === null || _a === void 0 ? void 0 : _a.currentTime) !== null && _b !== void 0 ? _b : 0);
this.timeRAF = window.requestAnimationFrame(() => {
this.requestTimeUpdates();
});
}
getMediaType() {
const { currentSrc } = this.mediaEl;
if (audioRegex.test(currentSrc))
return MediaType.Audio;
if (videoRegex.test(currentSrc) || hlsRegex.test(currentSrc))
return MediaType.Video;
return undefined;
}
onLoadedMetadata() {
this.mediaEl.volume = this.volume / 100;
this.listenToTextTracksForChanges();
this.onTextTracksChange();
this.onProgress();
this.dispatch('currentPoster', this.poster);
this.dispatch('duration', this.mediaEl.duration);
this.dispatch('playbackRates', this.playbackRates);
if (!this.willAttach) {
this.dispatch('currentSrc', this.mediaEl.currentSrc);
this.dispatch('mediaType', this.getMediaType());
this.dispatch('playbackReady', true);
}
}
onProgress() {
const { buffered, duration } = this.mediaEl;
const end = buffered.length === 0 ? 0 : buffered.end(buffered.length - 1);
this.dispatch('buffered', end > duration ? duration : end);
}
onPlay() {
this.requestTimeUpdates();
this.dispatch('paused', false);
if (!this.playbackStarted)
this.dispatch('playbackStarted', true);
}
onPause() {
this.cancelTimeUpdates();
this.dispatch('paused', true);
this.dispatch('buffering', false);
}
onPlaying() {
this.dispatch('playing', true);
this.dispatch('buffering', false);
}
onSeeking() {
if (!this.wasPausedBeforeSeeking)
this.wasPausedBeforeSeeking = this.mediaEl.paused;
this.dispatch('currentTime', this.mediaEl.currentTime);
this.dispatch('seeking', true);
}
onSeeked() {
// Avoid calling `attemptToPlay` if seeking to 0 on 0.
if (this.currentTime === 0 && !this.playbackStarted)
return;
this.dispatch('seeking', false);
if (!this.playbackStarted || !this.wasPausedBeforeSeeking)
this.attemptToPlay();
this.wasPausedBeforeSeeking = true;
}
onRateChange() {
this.dispatch('playbackRate', this.mediaEl.playbackRate);
}
onVolumeChange() {
this.dispatch('muted', this.mediaEl.muted);
this.dispatch('volume', this.mediaEl.volume * 100);
}
onDurationChange() {
this.dispatch('duration', this.mediaEl.duration);
}
onWaiting() {
this.dispatch('buffering', true);
}
onSuspend() {
this.dispatch('buffering', false);
}
onEnded() {
if (!this.loop)
this.dispatch('playbackEnded', true);
}
onError() {
this.vmError.emit(this.mediaEl.error);
}
attemptToPlay() {
var _a;
try {
(_a = this.mediaEl) === null || _a === void 0 ? void 0 : _a.play();
}
catch (e) {
this.vmError.emit(e);
}
}
togglePiPInChrome(toggle) {
var _a;
return toggle
? (_a = this.mediaEl) === null || _a === void 0 ? void 0 : _a.requestPictureInPicture()
: document.exitPictureInPicture();
}
togglePiPInSafari(toggle) {
var _a, _b;
const mode = toggle
? "picture-in-picture" /* PiP */
: "inline" /* Inline */;
if (!((_a = this.mediaEl) === null || _a === void 0 ? void 0 : _a.webkitSupportsPresentationMode(mode))) {
throw new Error('PiP API is not available.');
}
return (_b = this.mediaEl) === null || _b === void 0 ? void 0 : _b.webkitSetPresentationMode(mode);
}
togglePiP(toggle) {
return __awaiter(this, void 0, void 0, function* () {
if (canUsePiPInChrome())
return this.togglePiPInChrome(toggle);
if (canUsePiPInSafari())
return this.togglePiPInSafari(toggle);
throw new Error('PiP API is not available.');
});
}
onEnterPiP() {
this.dispatch('isPiPActive', true);
}
onLeavePiP() {
this.dispatch('isPiPActive', false);
}
addPresentationControllerListeners() {
this.presentationController.on('change', mode => {
this.dispatch('isPiPActive', mode === "picture-in-picture" /* PiP */);
this.dispatch('isFullscreenActive', mode === "fullscreen" /* Fullscreen */);
});
}
/** @internal */
getAdapter() {
return __awaiter(this, void 0, void 0, function* () {
return {
getInternalPlayer: () => __awaiter(this, void 0, void 0, function* () { return this.mediaEl; }),
play: () => __awaiter(this, void 0, void 0, function* () { var _a; return (_a = this.mediaEl) === null || _a === void 0 ? void 0 : _a.play(); }),
pause: () => __awaiter(this, void 0, void 0, function* () { var _b; return (_b = this.mediaEl) === null || _b === void 0 ? void 0 : _b.pause(); }),
canPlay: (type) => __awaiter(this, void 0, void 0, function* () { return isString(type) && (audioRegex.test(type) || videoRegex.test(type)); }),
setCurrentTime: (time) => __awaiter(this, void 0, void 0, function* () {
if (this.mediaEl)
this.mediaEl.currentTime = time;
}),
setMuted: (muted) => __awaiter(this, void 0, void 0, function* () {
if (this.mediaEl)
this.mediaEl.muted = muted;
}),
setVolume: (volume) => __awaiter(this, void 0, void 0, function* () {
if (this.mediaEl)
this.mediaEl.volume = volume / 100;
}),
canSetPlaybackRate: () => __awaiter(this, void 0, void 0, function* () { return true; }),
setPlaybackRate: (rate) => __awaiter(this, void 0, void 0, function* () {
if (this.mediaEl)
this.mediaEl.playbackRate = rate;
}),
canSetPiP: () => __awaiter(this, void 0, void 0, function* () { return canUsePiP(); }),
enterPiP: () => this.togglePiP(true),
exitPiP: () => this.togglePiP(false),
canSetFullscreen: () => __awaiter(this, void 0, void 0, function* () { return this.fullscreenController.isSupported; }),
enterFullscreen: () => this.fullscreenController.requestFullscreen(),
exitFullscreen: () => this.fullscreenController.exitFullscreen(),
setCurrentTextTrack: (trackId) => __awaiter(this, void 0, void 0, function* () {
if (trackId !== this.currentTextTrack)
this.toggleTextTrackModes(trackId);
}),
setTextTrackVisibility: (isVisible) => __awaiter(this, void 0, void 0, function* () {
this.isTextTrackVisible = isVisible;
this.toggleTextTrackModes(this.currentTextTrack);
}),
};
});
}
onHasCustomTextManagerChange() {
if (this.hasCustomTextManager) {
this.textTracksDisposal.empty();
}
else if (this.playbackReady) {
this.listenToTextTracksForChanges();
}
}
onShouldRenderNativeTextTracksChange() {
if (this.hasCustomTextManager)
return;
this.toggleTextTrackModes(this.currentTextTrack);
}
onProviderConnect(event) {
if (this.noConnect)
event.stopImmediatePropagation();
}
onProviderDisconnect(event) {
if (this.noConnect)
event.stopImmediatePropagation();
}
getFilteredTextTracks() {
const tracks = [];
const textTrackList = Array.from(this.mediaEl.textTracks);
for (let i = 0; i < textTrackList.length; i += 1) {
const track = textTrackList[i];
// Edge adds a track without a label; we don't want to use it.
if ((track.kind === 'subtitles' || track.kind === 'captions') &&
track.label) {
tracks.push(textTrackList[i]);
}
}
return tracks;
}
listenToTextTracksForChanges() {
if (this.hasCustomTextManager)
return;
this.textTracksDisposal.empty();
if (isUndefined(this.mediaEl))
return;
this.textTracksDisposal.add(listen(this.mediaEl.textTracks, 'change', this.onTextTracksChange.bind(this)));
}
onTextTracksChange() {
var _a;
const tracks = this.getFilteredTextTracks();
let trackId = -1;
for (let id = 0; id < tracks.length; id += 1) {
if (tracks[id].mode === 'hidden') {
// Do not break in case there is a following track with showing.
trackId = id;
}
else if (tracks[id].mode === 'showing') {
trackId = id;
break;
}
}
if (!this.shouldRenderNativeTextTracks &&
((_a = tracks[trackId]) === null || _a === void 0 ? void 0 : _a.mode) === 'showing') {
tracks[trackId].mode = 'hidden';
return;
}
if (this.shouldRenderNativeTextTracks) {
this.isTextTrackVisible =
trackId !== -1 && tracks[trackId].mode === 'showing';
this.dispatch('isTextTrackVisible', this.isTextTrackVisible);
}
this.dispatch('textTracks', tracks);
this.dispatch('currentTextTrack', this.shouldRenderNativeTextTracks && !this.isTextTrackVisible
? -1
: trackId);
}
toggleTextTrackModes(newTrackId) {
if (isNil(this.mediaEl))
return;
const { textTracks } = this.mediaEl;
if (newTrackId === -1) {
Array.from(textTracks).forEach(track => {
track.mode = 'disabled';
});
}
else {
const oldTrack = textTracks[this.currentTextTrack];
if (oldTrack)
oldTrack.mode = 'disabled';
}
const nextTrack = textTracks[newTrackId];
if (nextTrack) {
nextTrack.mode =
this.isTextTrackVisible && this.shouldRenderNativeTextTracks
? 'showing'
: 'hidden';
}
this.dispatch('currentTextTrack', this.shouldRenderNativeTextTracks && !this.isTextTrackVisible
? -1
: newTrackId);
this.dispatch('isTextTrackVisible', this.isTextTrackVisible);
}
render() {
const mediaProps = {
autoplay: this.autoplay,
muted: this.muted,
playsinline: this.playsinline,
playsInline: this.playsinline,
'x5-playsinline': this.playsinline,
'webkit-playsinline': this.playsinline,
controls: this.controls,
crossorigin: this.crossOrigin === '' ? 'anonymous' : this.crossOrigin,
controlslist: this.controlsList,
'data-poster': !this.hasCustomPoster() ? this.poster : undefined,
loop: this.loop,
preload: this.preload,
disablePictureInPicture: this.disablePiP,
autoPictureInPicture: this.autoPiP,
disableRemotePlayback: this.disableRemotePlayback,
'x-webkit-airplay': this.disableRemotePlayback ? 'deny' : 'allow',
ref: (el) => {
this.mediaEl = el;
},
onLoadedMetadata: this.onLoadedMetadata.bind(this),
onProgress: this.onProgress.bind(this),
onPlay: this.onPlay.bind(this),
onPause: this.onPause.bind(this),
onPlaying: this.onPlaying.bind(this),
onSeeking: this.onSeeking.bind(this),
onSeeked: this.onSeeked.bind(this),
onRateChange: this.onRateChange.bind(this),
onVolumeChange: this.onVolumeChange.bind(this),
onDurationChange: this.onDurationChange.bind(this),
onWaiting: this.onWaiting.bind(this),
onSuspend: this.onSuspend.bind(this),
onEnded: this.onEnded.bind(this),
onError: this.onError.bind(this),
};
const audio = (h("audio", Object.assign({ class: "lazy" }, mediaProps), h("slot", null), "Your browser does not support the", h("code", null, "audio"), "element."));
const video = (h("video", Object.assign({ class: "lazy" }, mediaProps, {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
onenterpictureinpicture: this.onEnterPiP.bind(this), onleavepictureinpicture: this.onLeavePiP.bind(this)
}), h("slot", null), "Your browser does not support the", h("code", null, "video"), "element."));
return this.viewType === ViewType.Audio ? audio : video;
}
get host() { return getElement(this); }
static get watchers() { return {
"mediaTitle": ["onMediaTitleChange"],
"poster": ["onPosterChange"],
"viewType": ["onViewTypeChange"],
"hasCustomTextManager": ["onHasCustomTextManagerChange"],
"shouldRenderNativeTextTracks": ["onShouldRenderNativeTextTracksChange"]
}; }
};
File.style = fileCss;
export { File as vm_file };