vidstack
Version:
Build awesome media experiences on the web.
3,459 lines • 129 kB
JavaScript
import { signal, effect, createContext, hasProvidedContext, useContext, provideContext, peek, isWriteSignal, StoreFactory, useStore, computed, onDispose, scoped, tick } from 'maverick.js';
import { ComponentController, Component, defineElement, prop, defineProp, method } from 'maverick.js/element';
import { s as scopedRaf, A as ARIAKeyShortcuts, a as setAttributeIfEmpty, o as onPress, u as useMedia, b as setARIALabel, i as isTrackCaptionKind, I as IS_SAFARI, f as functionThrottle, c as findActiveCue, d as canChangeVolume, e as onTrackChapterChange, g as isElementParent, h as isCueActive, p as preconnect } from './media-core.js';
import { $$_create_component, $$_create_walker, $$_effect, $$_attr, $$_next_element, $$_create_template, $$_listen, $$_ref, $$_setup_custom_element, $$_insert_at_marker_lite, $$_peek, $$_clone, $$_insert_lite, $$_computed, $$_next_template, $$_scoped } from 'maverick.js/dom';
import { I as Icon } from './icons/icon.js';
import { listenEvent, setAttribute, ariaBool as ariaBool$1, isNull, isNumber, setStyle, isDOMEvent, isKeyboardEvent, isUndefined, animationFrameThrottle, isDOMElement, isPointerEvent, isMouseEvent, isTouchEvent, kebabToCamelCase, createDisposalBin } from 'maverick.js/std';
import { CaptionsRenderer, renderVTTCueString, updateTimedVTTCueNodes } from 'media-captions';
function round(num, decimalPlaces = 2) {
return Number(num.toFixed(decimalPlaces));
}
function getNumberOfDecimalPlaces(num) {
return String(num).split(".")[1]?.length ?? 0;
}
function clampNumber(min, value, max) {
return Math.max(min, Math.min(max, value));
}
let $keyboard = signal(false);
{
listenEvent(document, "pointerdown", () => {
$keyboard.set(false);
});
listenEvent(document, "keydown", (e) => {
if (e.metaKey || e.altKey || e.ctrlKey)
return;
$keyboard.set(true);
});
}
class FocusVisibleController extends ComponentController {
_focused = signal(false);
onConnect(el) {
effect(() => {
if (!$keyboard()) {
this._focused.set(false);
updateFocusAttr(el, false);
this.listen("pointerenter", this._onPointerEnter.bind(this));
this.listen("pointerleave", this._onPointerLeave.bind(this));
return;
}
const active = document.activeElement === el;
this._focused.set(active);
updateFocusAttr(el, active);
this.listen("focus", this._onFocus.bind(this));
this.listen("blur", this._onBlur.bind(this));
});
}
focused() {
return this._focused();
}
_onFocus() {
this._focused.set(true);
updateFocusAttr(this.el, true);
}
_onBlur() {
this._focused.set(false);
updateFocusAttr(this.el, false);
}
_onPointerEnter() {
updateHoverAttr(this.el, true);
}
_onPointerLeave() {
updateHoverAttr(this.el, false);
}
}
function updateFocusAttr(el, isFocused) {
setAttribute(el, "data-focus", isFocused);
setAttribute(el, "data-hocus", isFocused);
}
function updateHoverAttr(el, isHovering) {
setAttribute(el, "data-hocus", isHovering);
setAttribute(el, "data-hover", isHovering);
}
const tooltipContext = createContext();
class Tooltip extends Component {
static el = defineElement({
tagName: "media-tooltip",
props: { position: "top center" }
});
onAttach(el) {
if (hasProvidedContext(tooltipContext)) {
scopedRaf(() => {
if (!el.isConnected)
return;
const tooltip = useContext(tooltipContext);
tooltip._attachTooltip(el);
});
}
this.setAttributes({
position: this.$props.position
});
}
}
let id = 0;
class TooltipController extends ComponentController {
constructor(instance) {
super(instance);
provideContext(tooltipContext, {
_attachTooltip: this._attachTooltip.bind(this)
});
}
_attachTooltip(tooltipEl) {
const tooltipId = `media-tooltip-${++id}`;
setAttribute(this.el, "aria-describedby", tooltipId);
setAttribute(tooltipEl, "id", tooltipId);
setAttribute(tooltipEl, "role", "tooltip");
this.el.removeAttribute("aria-label");
}
}
var __defProp$3 = Object.defineProperty;
var __getOwnPropDesc$3 = Object.getOwnPropertyDescriptor;
var __decorateClass$3 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$3(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$3(target, key, result);
return result;
};
const toggleButtonProps = {
disabled: false,
defaultPressed: false,
defaultAppearance: false
};
class ToggleButton extends Component {
static el = defineElement({
tagName: "media-toggle-button",
props: toggleButtonProps
});
_pressed = signal(false);
_keyShortcut;
constructor(instance) {
super(instance);
new FocusVisibleController(instance);
new TooltipController(instance);
if (this._keyShortcut)
new ARIAKeyShortcuts(instance, this._keyShortcut);
}
get pressed() {
return peek(this._pressed);
}
onAttach(el) {
if (isWriteSignal(this._pressed)) {
this._pressed.set(this.$props.defaultPressed());
}
setAttributeIfEmpty(el, "tabindex", "0");
setAttributeIfEmpty(el, "role", "button");
const { disabled, defaultAppearance } = this.$props;
this.setAttributes({
disabled,
"default-appearance": defaultAppearance,
"data-pressed": this._pressed,
"aria-pressed": this._isARIAPressed.bind(this),
"data-media-button": true
});
}
onConnect(el) {
onPress(el, this._onMaybePress.bind(this));
}
_isARIAPressed() {
return ariaBool$1(this._pressed());
}
_onPress(event) {
if (isWriteSignal(this._pressed)) {
this._pressed.set((p) => !p);
}
}
_onMaybePress(event) {
const disabled = this.$props.disabled();
if (disabled) {
if (disabled)
event.stopImmediatePropagation();
return;
}
event.preventDefault();
this._onPress(event);
}
}
__decorateClass$3([
prop
], ToggleButton.prototype, "pressed", 1);
var Icon$56 = `<path d="M8.66667 6.66667C8.29848 6.66667 8 6.96514 8 7.33333V24.6667C8 25.0349 8.29848 25.3333 8.66667 25.3333H12.6667C13.0349 25.3333 13.3333 25.0349 13.3333 24.6667V7.33333C13.3333 6.96514 13.0349 6.66667 12.6667 6.66667H8.66667Z" fill="currentColor"/> <path d="M19.3333 6.66667C18.9651 6.66667 18.6667 6.96514 18.6667 7.33333V24.6667C18.6667 25.0349 18.9651 25.3333 19.3333 25.3333H23.3333C23.7015 25.3333 24 25.0349 24 24.6667V7.33333C24 6.96514 23.7015 6.66667 23.3333 6.66667H19.3333Z" fill="currentColor"/>`;
var Icon$59 = `<path d="M10.6667 6.6548C10.6667 6.10764 11.2894 5.79346 11.7295 6.11862L24.377 15.4634C24.7377 15.7298 24.7377 16.2692 24.3771 16.5357L11.7295 25.8813C11.2895 26.2065 10.6667 25.8923 10.6667 25.3451L10.6667 6.6548Z" fill="currentColor"/>`;
var Icon$71 = `<path d="M15.6038 12.2147C16.0439 12.5399 16.6667 12.2257 16.6667 11.6786V10.1789C16.6667 10.1001 16.7351 10.0384 16.8134 10.0479C20.1116 10.4494 22.6667 13.2593 22.6667 16.6659C22.6667 20.3481 19.6817 23.3332 15.9995 23.3332C12.542 23.3332 9.69927 20.7014 9.36509 17.332C9.32875 16.9655 9.03371 16.6662 8.66548 16.6662L6.66655 16.6666C6.29841 16.6666 5.99769 16.966 6.02187 17.3334C6.36494 22.5454 10.7012 26.6667 16 26.6667C21.5228 26.6667 26 22.1895 26 16.6667C26 11.4103 21.9444 7.10112 16.7916 6.69757C16.7216 6.69209 16.6667 6.63396 16.6667 6.56372V4.98824C16.6667 4.44106 16.0439 4.12689 15.6038 4.45206L11.0765 7.79738C10.7159 8.06387 10.7159 8.60326 11.0766 8.86973L15.6038 12.2147Z" fill="currentColor"/>`;
class PlayButton extends ToggleButton {
static el = defineElement({
tagName: "media-play-button",
props: toggleButtonProps
});
_media;
_keyShortcut = "togglePaused";
onAttach(el) {
this._media = useMedia();
this._pressed = this._isPressed.bind(this);
super.onAttach(el);
setARIALabel(el, this._getLabel.bind(this));
const { paused, ended } = this._media.$store;
this.setAttributes({
"data-paused": paused,
"data-ended": ended
});
}
_onPress(event) {
const remote = this._media.remote;
this._pressed() ? remote.pause(event) : remote.play(event);
}
_isPressed() {
const { paused } = this._media.$store;
return !paused();
}
_getLabel() {
const { paused } = this._media.$store;
return paused() ? "Play" : "Pause";
}
render() {
return [
$$_create_component(Icon, { paths: Icon$59, slot: "play" }),
$$_create_component(Icon, { paths: Icon$71, slot: "replay" }),
$$_create_component(Icon, { paths: Icon$56, slot: "pause" })
];
}
}
var Icon$24 = `<path d="M8 28.0003C8 27.6321 8.29848 27.3336 8.66667 27.3336H23.3333C23.7015 27.3336 24 27.6321 24 28.0003V29.3336C24 29.7018 23.7015 30.0003 23.3333 30.0003H8.66667C8.29848 30.0003 8 29.7018 8 29.3336V28.0003Z" fill="currentColor"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M4.66602 6.66699C4.29783 6.66699 3.99935 6.96547 3.99935 7.33366V24.667C3.99935 25.0352 4.29783 25.3337 4.66602 25.3337H27.3327C27.7009 25.3337 27.9994 25.0352 27.9994 24.667V7.33366C27.9994 6.96547 27.7009 6.66699 27.3327 6.66699H4.66602ZM8.66659 21.3333C8.2984 21.3333 7.99992 21.0349 7.99992 20.6667V11.3333C7.99992 10.9651 8.2984 10.6667 8.66659 10.6667H13.9999C14.3681 10.6667 14.6666 10.9651 14.6666 11.3333V12.6667C14.6666 13.0349 14.3681 13.3333 13.9999 13.3333H10.7999C10.7263 13.3333 10.6666 13.393 10.6666 13.4667V18.5333C10.6666 18.607 10.7263 18.6667 10.7999 18.6667H13.9999C14.3681 18.6667 14.6666 18.9651 14.6666 19.3333V20.6667C14.6666 21.0349 14.3681 21.3333 13.9999 21.3333H8.66659ZM17.9999 21.3333C17.6317 21.3333 17.3333 21.0349 17.3333 20.6667V11.3333C17.3333 10.9651 17.6317 10.6667 17.9999 10.6667H23.3333C23.7014 10.6667 23.9999 10.9651 23.9999 11.3333V12.6667C23.9999 13.0349 23.7014 13.3333 23.3333 13.3333H20.1333C20.0596 13.3333 19.9999 13.393 19.9999 13.4667V18.5333C19.9999 18.607 20.0596 18.6667 20.1333 18.6667H23.3333C23.7014 18.6667 23.9999 18.9651 23.9999 19.3333V20.6667C23.9999 21.0349 23.7014 21.3333 23.3333 21.3333H17.9999Z" fill="currentColor"/>`;
var Icon$25 = `<path fill-rule="evenodd" clip-rule="evenodd" d="M4.6661 6.66699C4.29791 6.66699 3.99943 6.96547 3.99943 7.33366V24.667C3.99943 25.0352 4.29791 25.3337 4.6661 25.3337H27.3328C27.701 25.3337 27.9994 25.0352 27.9994 24.667V7.33366C27.9994 6.96547 27.701 6.66699 27.3328 6.66699H4.6661ZM8.66667 21.3333C8.29848 21.3333 8 21.0349 8 20.6667V11.3333C8 10.9651 8.29848 10.6667 8.66667 10.6667H14C14.3682 10.6667 14.6667 10.9651 14.6667 11.3333V12.6667C14.6667 13.0349 14.3682 13.3333 14 13.3333H10.8C10.7264 13.3333 10.6667 13.393 10.6667 13.4667V18.5333C10.6667 18.607 10.7264 18.6667 10.8 18.6667H14C14.3682 18.6667 14.6667 18.9651 14.6667 19.3333V20.6667C14.6667 21.0349 14.3682 21.3333 14 21.3333H8.66667ZM18 21.3333C17.6318 21.3333 17.3333 21.0349 17.3333 20.6667V11.3333C17.3333 10.9651 17.6318 10.6667 18 10.6667H23.3333C23.7015 10.6667 24 10.9651 24 11.3333V12.6667C24 13.0349 23.7015 13.3333 23.3333 13.3333H20.1333C20.0597 13.3333 20 13.393 20 13.4667V18.5333C20 18.607 20.0597 18.6667 20.1333 18.6667H23.3333C23.7015 18.6667 24 18.9651 24 19.3333V20.6667C24 21.0349 23.7015 21.3333 23.3333 21.3333H18Z" fill="currentColor"/>`;
function ariaBool(value) {
return value ? "true" : "false";
}
function $ariaBool(signal) {
return () => ariaBool(signal());
}
class CaptionButton extends ToggleButton {
static el = defineElement({
tagName: "media-caption-button",
props: toggleButtonProps
});
_media;
_keyShortcut = "toggleCaptions";
onAttach(el) {
this._media = useMedia();
this._pressed = this._isPressed.bind(this);
super.onAttach(el);
setARIALabel(el, this._getLabel.bind(this));
this.setAttributes({
"aria-hidden": $ariaBool(this._isHidden.bind(this))
});
}
_onPress(event) {
this._media.remote.toggleCaptions(event);
}
_isPressed() {
const { textTrack } = this._media.$store, track = textTrack();
return !!track && isTrackCaptionKind(track);
}
_isHidden() {
const { textTracks } = this._media.$store;
return textTracks().filter(isTrackCaptionKind).length == 0;
}
_getLabel() {
const { textTrack } = this._media.$store;
return textTrack() ? "Closed-Captions Off" : "Closed-Captions On";
}
render() {
return [
$$_create_component(Icon, { paths: Icon$24, slot: "on" }),
$$_create_component(Icon, { paths: Icon$25, slot: "off" })
];
}
}
var Icon$37 = `<path d="M19.3334 13.3333C18.9652 13.3333 18.6667 13.0349 18.6667 12.6667L18.6667 7.33333C18.6667 6.96514 18.9652 6.66666 19.3334 6.66666H21.3334C21.7015 6.66666 22 6.96514 22 7.33333V9.86666C22 9.9403 22.0597 10 22.1334 10L24.6667 10C25.0349 10 25.3334 10.2985 25.3334 10.6667V12.6667C25.3334 13.0349 25.0349 13.3333 24.6667 13.3333L19.3334 13.3333Z" fill="currentColor"/> <path d="M13.3334 19.3333C13.3334 18.9651 13.0349 18.6667 12.6667 18.6667H7.33335C6.96516 18.6667 6.66669 18.9651 6.66669 19.3333V21.3333C6.66669 21.7015 6.96516 22 7.33335 22H9.86669C9.94032 22 10 22.0597 10 22.1333L10 24.6667C10 25.0349 10.2985 25.3333 10.6667 25.3333H12.6667C13.0349 25.3333 13.3334 25.0349 13.3334 24.6667L13.3334 19.3333Z" fill="currentColor"/> <path d="M18.6667 24.6667C18.6667 25.0349 18.9652 25.3333 19.3334 25.3333H21.3334C21.7015 25.3333 22 25.0349 22 24.6667V22.1333C22 22.0597 22.0597 22 22.1334 22H24.6667C25.0349 22 25.3334 21.7015 25.3334 21.3333V19.3333C25.3334 18.9651 25.0349 18.6667 24.6667 18.6667L19.3334 18.6667C18.9652 18.6667 18.6667 18.9651 18.6667 19.3333L18.6667 24.6667Z" fill="currentColor"/> <path d="M10.6667 13.3333H12.6667C13.0349 13.3333 13.3334 13.0349 13.3334 12.6667L13.3334 10.6667V7.33333C13.3334 6.96514 13.0349 6.66666 12.6667 6.66666H10.6667C10.2985 6.66666 10 6.96514 10 7.33333L10 9.86666C10 9.9403 9.94033 10 9.86669 10L7.33335 10C6.96516 10 6.66669 10.2985 6.66669 10.6667V12.6667C6.66669 13.0349 6.96516 13.3333 7.33335 13.3333L10.6667 13.3333Z" fill="currentColor"/>`;
var Icon$38 = `<path d="M25.3299 7.26517C25.2958 6.929 25.0119 6.66666 24.6667 6.66666H19.3334C18.9652 6.66666 18.6667 6.96514 18.6667 7.33333V9.33333C18.6667 9.70152 18.9652 10 19.3334 10L21.8667 10C21.9403 10 22 10.0597 22 10.1333V12.6667C22 13.0349 22.2985 13.3333 22.6667 13.3333H24.6667C25.0349 13.3333 25.3334 13.0349 25.3334 12.6667V7.33333C25.3334 7.31032 25.3322 7.28758 25.3299 7.26517Z" fill="currentColor"/> <path d="M22 21.8667C22 21.9403 21.9403 22 21.8667 22L19.3334 22C18.9652 22 18.6667 22.2985 18.6667 22.6667V24.6667C18.6667 25.0349 18.9652 25.3333 19.3334 25.3333L24.6667 25.3333C25.0349 25.3333 25.3334 25.0349 25.3334 24.6667V19.3333C25.3334 18.9651 25.0349 18.6667 24.6667 18.6667H22.6667C22.2985 18.6667 22 18.9651 22 19.3333V21.8667Z" fill="currentColor"/> <path d="M12.6667 22H10.1334C10.0597 22 10 21.9403 10 21.8667V19.3333C10 18.9651 9.70154 18.6667 9.33335 18.6667H7.33335C6.96516 18.6667 6.66669 18.9651 6.66669 19.3333V24.6667C6.66669 25.0349 6.96516 25.3333 7.33335 25.3333H12.6667C13.0349 25.3333 13.3334 25.0349 13.3334 24.6667V22.6667C13.3334 22.2985 13.0349 22 12.6667 22Z" fill="currentColor"/> <path d="M10 12.6667V10.1333C10 10.0597 10.0597 10 10.1334 10L12.6667 10C13.0349 10 13.3334 9.70152 13.3334 9.33333V7.33333C13.3334 6.96514 13.0349 6.66666 12.6667 6.66666H7.33335C6.96516 6.66666 6.66669 6.96514 6.66669 7.33333V12.6667C6.66669 13.0349 6.96516 13.3333 7.33335 13.3333H9.33335C9.70154 13.3333 10 13.0349 10 12.6667Z" fill="currentColor"/>`;
class FullscreenButton extends ToggleButton {
static el = defineElement({
tagName: "media-fullscreen-button",
props: {
...toggleButtonProps,
target: "prefer-media"
}
});
_media;
_keyShortcut = "toggleFullscreen";
onAttach(el) {
this._media = useMedia();
this._pressed = this._isPressed.bind(this);
super.onAttach(el);
setARIALabel(el, this._getLabel.bind(this));
const { fullscreen } = this._media.$store;
this.setAttributes({
"aria-hidden": $ariaBool(this._isHidden.bind(this)),
"data-fullscreen": fullscreen
});
}
_onPress(event) {
const remote = this._media.remote, target = this.$props.target();
this._pressed() ? remote.exitFullscreen(target, event) : remote.enterFullscreen(target, event);
}
_isPressed() {
const { fullscreen } = this._media.$store;
return fullscreen();
}
_isHidden() {
const { canFullscreen } = this._media.$store;
return !canFullscreen();
}
_getLabel() {
const { fullscreen } = this._media.$store;
return fullscreen() ? "Exit Fullscreen" : "Enter Fullscreen";
}
render() {
return [
$$_create_component(Icon, { paths: Icon$38, slot: "enter" }),
$$_create_component(Icon, { paths: Icon$37, slot: "exit" })
];
}
}
var Icon$52 = `<path d="M17.5091 24.6594C17.5091 25.2066 16.8864 25.5208 16.4463 25.1956L9.44847 20.0252C9.42553 20.0083 9.39776 19.9991 9.36923 19.9991H4.66667C4.29848 19.9991 4 19.7006 4 19.3325V12.6658C4 12.2976 4.29848 11.9991 4.66667 11.9991H9.37115C9.39967 11.9991 9.42745 11.99 9.45039 11.973L16.4463 6.8036C16.8863 6.47842 17.5091 6.79259 17.5091 7.33977L17.5091 24.6594Z" fill="currentColor"/> <path d="M28.8621 13.6422C29.1225 13.3818 29.1225 12.9597 28.8621 12.6994L27.9193 11.7566C27.659 11.4962 27.2368 11.4962 26.9765 11.7566L24.7134 14.0197C24.6613 14.0717 24.5769 14.0717 24.5248 14.0197L22.262 11.7568C22.0016 11.4964 21.5795 11.4964 21.3191 11.7568L20.3763 12.6996C20.116 12.9599 20.116 13.382 20.3763 13.6424L22.6392 15.9053C22.6913 15.9573 22.6913 16.0418 22.6392 16.0938L20.3768 18.3562C20.1165 18.6166 20.1165 19.0387 20.3768 19.299L21.3196 20.2419C21.58 20.5022 22.0021 20.5022 22.2624 20.2418L24.5248 17.9795C24.5769 17.9274 24.6613 17.9274 24.7134 17.9795L26.976 20.2421C27.2363 20.5024 27.6585 20.5024 27.9188 20.2421L28.8616 19.2992C29.122 19.0389 29.122 18.6168 28.8616 18.3564L26.599 16.0938C26.547 16.0418 26.547 15.9573 26.599 15.9053L28.8621 13.6422Z" fill="currentColor"/>`;
var Icon$101 = `<path d="M17.5091 24.6595C17.5091 25.2066 16.8864 25.5208 16.4463 25.1956L9.44847 20.0252C9.42553 20.0083 9.39776 19.9992 9.36923 19.9992H4.66667C4.29848 19.9992 4 19.7007 4 19.3325V12.6658C4 12.2976 4.29848 11.9992 4.66667 11.9992H9.37115C9.39967 11.9992 9.42745 11.99 9.45039 11.9731L16.4463 6.80363C16.8863 6.47845 17.5091 6.79262 17.5091 7.3398L17.5091 24.6595Z" fill="currentColor"/> <path d="M27.5091 9.33336C27.8773 9.33336 28.1758 9.63184 28.1758 10V22C28.1758 22.3682 27.8773 22.6667 27.5091 22.6667H26.1758C25.8076 22.6667 25.5091 22.3682 25.5091 22V10C25.5091 9.63184 25.8076 9.33336 26.1758 9.33336L27.5091 9.33336Z" fill="currentColor"/> <path d="M22.1758 12C22.544 12 22.8424 12.2985 22.8424 12.6667V19.3334C22.8424 19.7016 22.544 20 22.1758 20H20.8424C20.4743 20 20.1758 19.7016 20.1758 19.3334V12.6667C20.1758 12.2985 20.4743 12 20.8424 12H22.1758Z" fill="currentColor"/>`;
var Icon$102 = `<path d="M17.5091 24.6594C17.5091 25.2066 16.8864 25.5207 16.4463 25.1956L9.44847 20.0252C9.42553 20.0083 9.39776 19.9991 9.36923 19.9991H4.66667C4.29848 19.9991 4 19.7006 4 19.3324V12.6658C4 12.2976 4.29848 11.9991 4.66667 11.9991H9.37115C9.39967 11.9991 9.42745 11.99 9.45039 11.973L16.4463 6.80358C16.8863 6.4784 17.5091 6.79258 17.5091 7.33975L17.5091 24.6594Z" fill="currentColor"/> <path d="M22.8424 12.6667C22.8424 12.2985 22.544 12 22.1758 12H20.8424C20.4743 12 20.1758 12.2985 20.1758 12.6667V19.3333C20.1758 19.7015 20.4743 20 20.8424 20H22.1758C22.544 20 22.8424 19.7015 22.8424 19.3333V12.6667Z" fill="currentColor"/>`;
class MuteButton extends ToggleButton {
static el = defineElement({
tagName: "media-mute-button",
props: toggleButtonProps
});
_media;
_keyShortcut = "toggleMuted";
onAttach(el) {
this._media = useMedia();
this._pressed = this._isPressed.bind(this);
setARIALabel(el, this._getLabel.bind(this));
this.setAttributes({
"data-muted": this._pressed,
"data-volume": this._getVolumeText.bind(this)
});
super.onAttach(el);
}
_onPress(event) {
const remote = this._media.remote;
this._pressed() ? remote.unmute(event) : remote.mute(event);
}
_isPressed() {
const { muted, volume } = this._media.$store;
return muted() || volume() === 0;
}
_getLabel() {
return this._pressed() ? "Unmute" : "Mute";
}
_getVolumeText() {
const { muted, volume } = this._media.$store, $volume = volume();
if (muted() || $volume === 0)
return "muted";
else if ($volume >= 0.5)
return "high";
else if ($volume < 0.5)
return "low";
}
render() {
return [
$$_create_component(Icon, { paths: Icon$101, slot: "volume-high" }),
$$_create_component(Icon, { paths: Icon$102, slot: "volume-low" }),
$$_create_component(Icon, { paths: Icon$52, slot: "volume-muted" })
];
}
}
var Icon$57 = `<path d="M5.33334 26V19.4667C5.33334 19.393 5.39304 19.3333 5.46668 19.3333H7.86668C7.94031 19.3333 8.00001 19.393 8.00001 19.4667V23.3333C8.00001 23.7015 8.29849 24 8.66668 24H23.3333C23.7015 24 24 23.7015 24 23.3333V8.66666C24 8.29847 23.7015 7.99999 23.3333 7.99999H19.4667C19.393 7.99999 19.3333 7.9403 19.3333 7.86666V5.46666C19.3333 5.39302 19.393 5.33333 19.4667 5.33333H26C26.3682 5.33333 26.6667 5.63181 26.6667 5.99999V26C26.6667 26.3682 26.3682 26.6667 26 26.6667H6.00001C5.63182 26.6667 5.33334 26.3682 5.33334 26Z" fill="currentColor"/> <path d="M14.0098 8.42359H10.806C10.6872 8.42359 10.6277 8.56721 10.7117 8.6512L16.5491 14.4886C16.8094 14.7489 16.8094 15.171 16.5491 15.4314L15.3234 16.657C15.0631 16.9174 14.641 16.9174 14.3806 16.657L8.63739 10.9138C8.55339 10.8298 8.40978 10.8893 8.40978 11.0081V14.0236C8.40978 14.3918 8.1113 14.6903 7.74311 14.6903H6.00978C5.64159 14.6903 5.34311 14.3918 5.34311 14.0236L5.34311 6.02359C5.34311 5.6554 5.64159 5.35692 6.00978 5.35692L14.0098 5.35692C14.378 5.35692 14.6764 5.6554 14.6764 6.02359V7.75692C14.6764 8.12511 14.378 8.42359 14.0098 8.42359Z" fill="currentColor"/>`;
var Icon$58 = `<path d="M16 15.3333C15.6318 15.3333 15.3333 15.6318 15.3333 16V20C15.3333 20.3682 15.6318 20.6667 16 20.6667H21.3333C21.7015 20.6667 22 20.3682 22 20V16C22 15.6318 21.7015 15.3333 21.3333 15.3333H16Z" fill="currentColor"/> <path fill-rule="evenodd" clip-rule="evenodd" d="M5.33333 7.33334C5.33333 6.96515 5.63181 6.66667 5.99999 6.66667H26C26.3682 6.66667 26.6667 6.96515 26.6667 7.33334V24.6667C26.6667 25.0349 26.3682 25.3333 26 25.3333H5.99999C5.63181 25.3333 5.33333 25.0349 5.33333 24.6667V7.33334ZM7.99999 10C7.99999 9.63182 8.29847 9.33334 8.66666 9.33334H23.3333C23.7015 9.33334 24 9.63182 24 10V22C24 22.3682 23.7015 22.6667 23.3333 22.6667H8.66666C8.29847 22.6667 7.99999 22.3682 7.99999 22V10Z" fill="currentColor"/>`;
class PIPButton extends ToggleButton {
static el = defineElement({
tagName: "media-pip-button",
props: toggleButtonProps
});
_media;
_keyShortcut = "togglePictureInPicture";
onAttach(el) {
this._media = useMedia();
this._pressed = this._isPressed.bind(this);
super.onAttach(el);
setARIALabel(el, this._getLabel.bind(this));
const { pictureInPicture } = this._media.$store;
this.setAttributes({
"aria-hidden": $ariaBool(this._isHidden.bind(this)),
"data-pip": pictureInPicture
});
}
_onPress(event) {
const remote = this._media.remote;
this._pressed() ? remote.exitPictureInPicture(event) : remote.enterPictureInPicture(event);
}
_isPressed() {
const { pictureInPicture } = this._media.$store;
return pictureInPicture();
}
_isHidden() {
const { canPictureInPicture } = this._media.$store;
return !canPictureInPicture();
}
_getLabel() {
const { pictureInPicture } = this._media.$store;
return pictureInPicture() ? "Exit Picture In Picture" : "Enter Picture In Picture";
}
render() {
return [
$$_create_component(Icon, { paths: Icon$58, slot: "enter" }),
$$_create_component(Icon, { paths: Icon$57, slot: "exit" })
];
}
}
var Icon$77 = `<path d="M15.6038 12.2148C16.0439 12.5399 16.6667 12.2257 16.6667 11.6786V10.1789C16.6667 10.1001 16.7351 10.0384 16.8134 10.0479C20.1116 10.4494 22.6667 13.2593 22.6667 16.6659C22.6667 20.3481 19.6817 23.3332 15.9995 23.3332C12.542 23.3332 9.69927 20.7015 9.36509 17.332C9.32875 16.9655 9.03371 16.6662 8.66548 16.6662L6.66655 16.6666C6.29841 16.6666 5.99769 16.966 6.02187 17.3334C6.36494 22.5454 10.7012 26.6667 16 26.6667C21.5228 26.6667 26 22.1895 26 16.6667C26 11.4103 21.9444 7.10112 16.7916 6.69758C16.7216 6.69209 16.6667 6.63396 16.6667 6.56372V4.98824C16.6667 4.44106 16.0439 4.12689 15.6038 4.45207L11.0765 7.79738C10.7159 8.06387 10.7159 8.60327 11.0766 8.86974L15.6038 12.2148Z" fill="currentColor"/>`;
var Icon$81 = `<path d="M16.4167 12.2148C15.9766 12.5399 15.3538 12.2257 15.3538 11.6786V10.1789C15.3538 10.1001 15.2854 10.0384 15.2072 10.0479C11.9089 10.4494 9.35384 13.2593 9.35384 16.6659C9.35384 20.3481 12.3389 23.3332 16.0211 23.3332C19.4785 23.3332 22.3212 20.7015 22.6554 17.332C22.6918 16.9655 22.9868 16.6662 23.355 16.6662L25.354 16.6666C25.7221 16.6666 26.0228 16.966 25.9986 17.3334C25.6556 22.5454 21.3193 26.6667 16.0205 26.6667C10.4977 26.6667 6.02051 22.1895 6.02051 16.6667C6.02051 11.4103 10.0761 7.10112 15.2289 6.69758C15.2989 6.69209 15.3538 6.63396 15.3538 6.56372V4.98824C15.3538 4.44106 15.9766 4.12689 16.4167 4.45207L20.944 7.79738C21.3046 8.06387 21.3046 8.60327 20.9439 8.86974L16.4167 12.2148Z" fill="currentColor"/>`;
class SeekButton extends Component {
static el = defineElement({
tagName: "media-seek-button",
props: {
disabled: false,
defaultAppearance: false,
seconds: 30
}
});
_media;
constructor(instance) {
super(instance);
this._media = useMedia();
new FocusVisibleController(instance);
new TooltipController(instance);
}
onAttach(el) {
setAttributeIfEmpty(el, "tabindex", "0");
setAttributeIfEmpty(el, "role", "button");
setARIALabel(el, this._getLabel.bind(this));
const { seconds, defaultAppearance } = this.$props;
this.setAttributes({
seconds,
"default-appearance": defaultAppearance,
"aria-hidden": $ariaBool(this._isHidden.bind(this)),
"data-media-button": true
});
}
onConnect(el) {
onPress(el, this._onPress.bind(this));
}
_isHidden() {
const { canSeek } = this._media.$store;
return !canSeek();
}
_getLabel() {
const { seconds } = this.$props;
return `Seek ${seconds() > 0 ? "forward" : "backward"} ${seconds()} seconds`;
}
_onPress(event) {
const { seconds, disabled } = this.$props;
if (disabled())
return;
const { currentTime } = this._media.$store, seekTo = currentTime() + seconds();
this._media.remote.seek(seekTo, event);
}
render() {
return [
$$_create_component(Icon, { paths: Icon$77, slot: "backward" }),
$$_create_component(Icon, { paths: Icon$81, slot: "forward" })
];
}
}
const SliderStoreFactory = new StoreFactory({
min: 0,
max: 100,
value: 50,
pointerValue: 0,
focused: false,
dragging: false,
pointing: false,
get interactive() {
return this.dragging || this.focused || this.pointing;
},
get fillRate() {
return calcRate(this.min, this.max, this.value);
},
get fillPercent() {
return this.fillRate * 100;
},
get pointerRate() {
return calcRate(this.min, this.max, this.pointerValue);
},
get pointerPercent() {
return this.pointerRate * 100;
}
});
function calcRate(min, max, value) {
const range = max - min, offset = value - min;
return range > 0 ? offset / range : 0;
}
const sliderProps = {
min: 0,
max: 100,
disabled: false,
value: 100,
step: 1,
keyStep: 1,
shiftKeyMultiplier: 5,
trackClass: null,
trackFillClass: null,
trackProgressClass: null,
thumbContainerClass: null,
thumbClass: null
};
function getClampedValue(min, max, value, step) {
return clampNumber(min, round(value, getNumberOfDecimalPlaces(step)), max);
}
function getValueFromRate(min, max, rate, step) {
const boundRate = clampNumber(0, rate, 1), range = max - min, fill = range * boundRate, stepRatio = fill / step, steps = step * stepRatio;
return min + steps;
}
const SliderKeyDirection = {
Left: -1,
ArrowLeft: -1,
Up: 1,
ArrowUp: 1,
Right: 1,
ArrowRight: 1,
Down: -1,
ArrowDown: -1
};
class SliderEventsController extends ComponentController {
constructor(instance, _delegate, _media) {
super(instance);
this._delegate = _delegate;
this._media = _media;
}
onConnect() {
effect(this._attachEventListeners.bind(this));
effect(this._attachPointerListeners.bind(this));
if (this._delegate._swipeGesture) {
scopedRaf(() => {
const outlet = this._media.player?.querySelector("media-outlet");
if (outlet) {
this._outlet = outlet;
listenEvent(outlet, "touchstart", this._onTouchStart.bind(this));
listenEvent(outlet, "touchmove", this._onTouchMove.bind(this));
}
});
}
}
_outlet = null;
_touchX = null;
_touchStartValue = null;
_onTouchStart(event) {
this._touchX = event.touches[0].clientX;
}
_onTouchMove(event) {
if (isNull(this._touchX))
return;
event.preventDefault();
if (this.$store.dragging())
return;
const diff = event.touches[0].clientX - this._touchX;
if (Math.abs(diff) > 20) {
this._touchX = event.touches[0].clientX;
this._touchStartValue = this.$store.value();
this._onStartDragging(this._touchStartValue, event);
}
}
_attachEventListeners() {
if (this._delegate._isDisabled())
return;
this.listen("focus", this._onFocus.bind(this));
this.listen("pointerenter", this._onPointerEnter.bind(this));
this.listen("pointermove", this._onPointerMove.bind(this));
this.listen("pointerleave", this._onPointerLeave.bind(this));
this.listen("pointerdown", this._onPointerDown.bind(this));
this.listen("keydown", this._onKeyDown.bind(this));
this.listen("keyup", this._onKeyUp.bind(this));
}
_attachPointerListeners() {
if (this._delegate._isDisabled() || !this.$store.dragging())
return;
listenEvent(document, "pointerup", this._onDocumentPointerUp.bind(this));
listenEvent(document, "pointermove", this._onDocumentPointerMove.bind(this));
if (IS_SAFARI) {
listenEvent(document, "touchmove", this._onDocumentTouchMove.bind(this), {
passive: false
});
}
}
_onFocus() {
this._updatePointerValue(this.$store.value());
}
_updateValue(newValue, trigger) {
const { value, min, max, dragging } = this.$store;
const clampedValue = Math.max(min(), Math.min(newValue, max()));
value.set(clampedValue);
const event = this.createEvent("value-change", { detail: clampedValue, trigger });
this.el.dispatchEvent(event);
this._delegate._onValueChange(event);
if (dragging()) {
const event2 = this.createEvent("drag-value-change", { detail: clampedValue, trigger });
this.el.dispatchEvent(event2);
this._delegate._onDragValueChange(event2);
}
}
_updatePointerValue(value, trigger) {
const { pointerValue, dragging } = this.$store;
pointerValue.set(value);
this.dispatch("pointer-value-change", { detail: value, trigger });
if (dragging()) {
const dir = this._delegate._orientation === "vertical" ? "bottom" : "left", size = this._delegate._orientation === "vertical" ? "height" : "width";
if (this._trackFill && !this.el?.hasAttribute("data-chapters")) {
this._trackFill.style[size] = value + "%";
}
if (this._thumb) {
this._thumb.style[dir] = value + "%";
}
this._updateValue(value, trigger);
}
}
_getPointerValue(event) {
let thumbPositionRate, rect = this.el.getBoundingClientRect(), { min, max } = this.$store;
if (this._delegate._orientation === "vertical") {
const { bottom: trackBottom, height: trackHeight } = rect;
thumbPositionRate = (trackBottom - event.clientY) / trackHeight;
} else {
if (this._touchX && isNumber(this._touchStartValue)) {
const { width } = this._outlet.getBoundingClientRect(), rate = (event.clientX - this._touchX) / width, range = max() - min(), diff = range * Math.abs(rate);
thumbPositionRate = (rate < 0 ? this._touchStartValue - diff : this._touchStartValue + diff) / range;
} else {
const { left: trackLeft, width: trackWidth } = rect;
thumbPositionRate = (event.clientX - trackLeft) / trackWidth;
}
}
return Math.max(
min(),
Math.min(
max(),
this._delegate._roundValue(
getValueFromRate(min(), max(), thumbPositionRate, this._delegate._getStep())
)
)
);
}
_onPointerEnter(event) {
this.$store.pointing.set(true);
}
_onPointerMove(event) {
const { dragging } = this.$store;
if (dragging())
return;
this._updatePointerValue(this._getPointerValue(event), event);
}
_onPointerLeave(event) {
this.$store.pointing.set(false);
}
_onPointerDown(event) {
if (event.button !== 0)
return;
const value = this._getPointerValue(event);
this._onStartDragging(value, event);
this._updatePointerValue(value, event);
}
_thumb = null;
_trackFill = null;
_onStartDragging(value, trigger) {
const { dragging } = this.$store;
if (dragging())
return;
dragging.set(true);
this._thumb = this.el.querySelector(
'shadow-root > div[part="thumb-container"]'
);
this._trackFill = this.el.querySelector(
'shadow-root > div[part~="track-fill"]'
);
this._media.remote.pauseUserIdle(trigger);
const event = this.createEvent("drag-start", { detail: value, trigger });
this.el.dispatchEvent(event);
this._delegate._onDragStart(event);
}
_onStopDragging(value, trigger) {
const { dragging } = this.$store;
if (!dragging())
return;
dragging.set(false);
if (this._trackFill) {
setStyle(this._trackFill, "width", null);
this._trackFill = null;
}
if (this._thumb) {
setStyle(this._thumb, "left", null);
setStyle(this._thumb, "bottom", null);
this._thumb = null;
}
this._media.remote.resumeUserIdle(trigger);
const event = this.createEvent("drag-end", { detail: value, trigger });
this.el.dispatchEvent(event);
this._delegate._onDragEnd(event);
this._touchX = null;
this._touchStartValue = null;
}
// -------------------------------------------------------------------------------------------
// Keyboard Events
// -------------------------------------------------------------------------------------------
_lastDownKey;
_onKeyDown(event) {
if (isDOMEvent(event)) {
const trigger = event.trigger;
if (isKeyboardEvent(trigger))
event = trigger;
else
return;
}
const { key } = event;
const { min, max } = this.$store;
let newValue;
if (key === "Home" || key === "PageUp") {
newValue = min();
} else if (key === "End" || key === "PageDown") {
newValue = max();
} else if (!event.metaKey && /[0-9]/.test(key)) {
newValue = (max() - min()) / 10 * Number(key);
}
if (!isUndefined(newValue)) {
this._updatePointerValue(newValue, event);
this._updateValue(newValue, event);
return;
}
const value = this._getKeyValue(event);
if (!value)
return;
const repeat = key === this._lastDownKey;
if (!this.$store.dragging() && repeat)
this._onStartDragging(value, event);
this._updatePointerValue(value, event);
if (!repeat)
this._updateValue(value, event);
this._lastDownKey = key;
}
_onKeyUp(event) {
if (isDOMEvent(event)) {
const trigger = event.trigger;
if (isKeyboardEvent(trigger))
event = trigger;
else
return;
}
this._lastDownKey = "";
const { dragging, value } = this.$store;
if (!dragging())
return;
const newValue = this._getKeyValue(event) ?? value();
this._updatePointerValue(newValue);
this._onStopDragging(newValue, event);
}
_getKeyValue(event) {
const { key, shiftKey } = event, isValidKey = Object.keys(SliderKeyDirection).includes(key);
if (!isValidKey)
return;
const { shiftKeyMultiplier } = this.$props;
const { value } = this.$store, step = this._delegate._getStep(), keyStep = this._delegate._getKeyStep();
const modifiedStep = !shiftKey ? keyStep : keyStep * shiftKeyMultiplier(), direction = Number(SliderKeyDirection[key]), diff = modifiedStep * direction, steps = (value() + diff) / step;
return Number((step * steps).toFixed(3));
}
// -------------------------------------------------------------------------------------------
// Document (Pointer Events)
// -------------------------------------------------------------------------------------------
_onDocumentPointerUp(event) {
if (event.button !== 0)
return;
const value = this._getPointerValue(event);
this._updatePointerValue(value, event);
this._onStopDragging(value, event);
}
_onDocumentTouchMove(event) {
event.preventDefault();
}
_onDocumentPointerMove = functionThrottle(
(event) => {
this._updatePointerValue(this._getPointerValue(event), event);
},
20,
{ leading: true }
);
}
const sliderValueFormatContext = createContext(() => ({}));
const $$_templ$h = /* @__PURE__ */ $$_create_template(`<!$><div part="track"></div>`), $$_templ_2$6 = /* @__PURE__ */ $$_create_template(`<!$><div part="track track-fill"></div>`), $$_templ_3$1 = /* @__PURE__ */ $$_create_template(`<!$><div part="track track-progress"></div>`), $$_templ_4 = /* @__PURE__ */ $$_create_template(`<!$><div part="thumb-container"><!$><div part="thumb"></div></div>`);
class Slider extends Component {
static el = defineElement({
tagName: "media-slider",
props: sliderProps,
store: SliderStoreFactory
});
_media;
_readonly = false;
_orientation = "";
constructor(instance) {
super(instance);
provideContext(sliderValueFormatContext);
this._media = useMedia();
new SliderEventsController(instance, this, this._media);
const focus = new FocusVisibleController(instance);
this.$store.focused = focus.focused.bind(focus);
}
onAttach(el) {
setAttributeIfEmpty(el, "role", "slider");
setAttributeIfEmpty(el, "tabindex", "0");
setAttributeIfEmpty(el, "aria-orientation", "horizontal");
setAttributeIfEmpty(el, "autocomplete", "off");
this._orientation = el.getAttribute("aria-orientation") || "";
if (!this._readonly) {
effect(this._watchMinMax.bind(this));
effect(this._watchDisabled.bind(this));
}
effect(this._watchValue.bind(this));
this._setAttrs();
}
onConnect(el) {
this._setupPreview();
}
render() {
const { trackClass, trackFillClass, trackProgressClass, thumbContainerClass, thumbClass } = this.$props;
return [
(() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$h);
$$_effect(() => $$_attr($$_root, "class", trackClass()));
return $$_root;
})(),
(() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_2$6);
$$_effect(() => $$_attr($$_root, "class", trackFillClass()));
return $$_root;
})(),
(() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_3$1);
$$_effect(() => $$_attr($$_root, "class", trackProgressClass()));
return $$_root;
})(),
(() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_4), $$_el = $$_next_element($$_walker);
$$_effect(() => $$_attr($$_root, "class", thumbContainerClass()));
$$_effect(() => $$_attr($$_el, "class", thumbClass()));
return $$_root;
})()
];
}
// -------------------------------------------------------------------------------------------
// Props
// -------------------------------------------------------------------------------------------
_getStep() {
return this.$props.step();
}
_getKeyStep() {
return this.$props.keyStep();
}
_roundValue(value) {
return Math.round(value);
}
_isDisabled() {
return this.$props.disabled();
}
// -------------------------------------------------------------------------------------------
// Watch
// -------------------------------------------------------------------------------------------
_watchMinMax() {
const { min, max } = this.$props;
this.$store.min.set(min());
this.$store.max.set(max());
}
_watchDisabled() {
if (!this._isDisabled())
return;
const { dragging, pointing } = this.$store;
dragging.set(false);
pointing.set(false);
}
_watchValue() {
const { dragging, value, min, max } = this.$store;
if (peek(dragging))
return;
value.set(getClampedValue(min(), max(), value(), this._getStep()));
}
// -------------------------------------------------------------------------------------------
// ARIA
// -------------------------------------------------------------------------------------------
_getARIADisabled() {
return ariaBool$1(this._isDisabled());
}
_getARIAValueMin() {
return this.$props.min();
}
_getARIAValueMax() {
return this.$props.max();
}
_getARIAValueNow() {
const { value } = this.$store;
return Math.round(value());
}
_getARIAValueText() {
const { value, max } = this.$store;
return round(value() / max() * 100, 2) + "%";
}
// -------------------------------------------------------------------------------------------
// Attributes
// -------------------------------------------------------------------------------------------
_setAttrs() {
const { disabled } = this.$props, { dragging, interactive, pointing } = this.$store;
this.setAttributes({
disabled,
"data-dragging": dragging,
"data-pointing": pointing,
"data-interactive": interactive,
"aria-disabled": this._getARIADisabled.bind(this),
"aria-valuemin": this._getARIAValueMin.bind(this),
"aria-valuemax": this._getARIAValueMax.bind(this),
"aria-valuenow": this._getARIAValueNow.bind(this),
"aria-valuetext": this._getARIAValueText.bind(this),
"data-styled": this._isStyled.bind(this),
"data-media-slider": true
});
effect(this._watchCSSVars.bind(this));
}
_isStyled() {
return !!this.$props.trackClass();
}
_watchCSSVars() {
const { fillPercent, pointerPercent } = this.$store;
this._updateSliderVars(round(fillPercent(), 3), round(pointerPercent(), 3));
}
_updateSliderVars = animationFrameThrottle(
(fillPercent, pointerPercent) => {
this.el?.style.setProperty("--slider-fill-percent", fillPercent + "%");
this.el?.style.setProperty("--slider-pointer-percent", pointerPercent + "%");
}
);
// -------------------------------------------------------------------------------------------
// Preview
// -------------------------------------------------------------------------------------------
_preview = null;
_setupPreview() {
this._preview = this.el.querySelector('[slot="preview"]');
if (!this._preview)
return;
effect(this._watchPreview.bind(this));
Promise.resolve().then(function () { return preview; }).then(({ setupPreviewStyles }) => {
setupPreviewStyles(this._preview, this._orientation);
});
}
_watchPreview() {
if (this._isDisabled() || !this._preview)
return;
window.requestAnimationFrame(this._onPreviewResize);
const observer = new ResizeObserver(this._onPreviewResize);
observer.observe(this._preview);
return () => observer.disconnect();
}
_onPreviewResize = animationFrameThrottle(() => {
if (!this._preview)
return;
const rect = this._preview.getBoundingClientRect();
setStyle(this._preview, "--computed-width", rect.width + "px");
setStyle(this._preview, "--computed-height", rect.height + "px");
});
// -------------------------------------------------------------------------------------------
// Events
// -------------------------------------------------------------------------------------------
_onValueChange(event) {
}
_onDragStart(event) {
}
_onDragEnd(event) {
}
_onDragValueChange(event) {
}
}
const $$_templ$g = /* @__PURE__ */ $$_create_template(`<!$><img part="img" loading="eager" decoding="async" aria-hidden="true" />`);
class Thumbnail extends Component {
static el = defineElement({
tagName: "media-thumbnail",
props: { time: 0 }
});
_media;
_img = null;
_coords = null;
_styleResets = [];
_src = signal("");
_loaded = signal(false);
_activeCue = signal(null);
constructor(instance) {
super(instance);
this._media = useMedia();
}
onAttach() {
this.setAttributes({
"data-loading": this._isLoading.bind(this),
"aria-hidden": $ariaBool(this._isHidden.bind(this))
});
}
onConnect() {
effect(this._onLoadStart.bind(this));
effect(this._onFindActiveCue.bind(this));
effect(this._onResolveThumbnail.bind(this));
}
_onLoadStart() {
this._src();
this._media.$store.thumbnails();
this._loaded.set(false);
}
_onLoaded() {
this._loaded.set(true);
this._requestResize();
}
_isLoading() {
return !this._isHidden() && !this._loaded();
}
_isHidden() {
const { duration, thumbnailCues } = this._media.$store;
return !Number.isFinite(duration()) || thumbnailCues().length === 0;
}
_onFindActiveCue() {
const { time } = this.$props, { duration, thumbnailCues } = this._media.$store, _cues = thumbnailCues(), _time = time();
if (!_cues || !Number.isFinite(duration())) {
this._activeCue.set(null);
return;
}
this._activeCue.set(findActiveCue(_time, _cues));
}
_onImgRef(el) {
this._img = el;
}
_onResolveThumbnail() {
const activeCue = this._activeCue(), thumbnails = peek(this._media.$store.thumbnails);
if (!thumbnails || !activeCue) {
this._src.set("");
this._resetStyles();
return;
}
const [_src, _coords = ""] = (activeCue.text || "").split("#");
this._coords = this._resolveThumbnailCoords(_coords);
if (!this._coords) {
this._resetStyles();
return;
}
this._src.set(this._resolveThumbnailSrc(thumbnails, _src));
this._requestResize();
}
_resolveThumbnailSrc(baseURL, src) {
return !/https?:/.test(src) ? `${baseURL.split("/").slice(0, -1).join("/")}${src.replace(/^\/?/, "/")}`.replace(
/^\/\//,
"/"
) : src;
}
_resolveThumbnailCoords(coords) {
const [props, values] = coords.split("="), resolvedCoords = {}, coordValues = values?.split(",");
if (!props || !values)
return null;
for (let i = 0; i < props.length; i++)
resolvedCoords[props[i]] = +coordValues[i];
return resolvedCoords;
}
_requestResize = animationFrameThrottle(this._resize.bind(this));
_resize() {
if (!this._img || !this._coords || !this.el)
return;
const { w, h, x, y } = this._coords, { maxWidth, maxHeight, minWidth, minHeight } = getComputedStyle(this.el), minRatio = Math.max(parseInt(minWidth) / w, parseInt(minHeight) / h), maxRatio = Math.min(parseInt(maxWidth) / w, parseInt(maxHeight) / h), scale = maxRatio < 1 ? maxRatio : minRatio > 1 ? minRatio : 1;
this._style(this.el, "--thumbnail-width", `${w * scale}px`);
this._style(this.el, "--thumbnail-height", `${h * scale}px`);
this._style(this._img, "width", `${this._img.naturalWidth * scale}px`);
this._style(this._img, "height", `${this._img.naturalHeight * scale}px`);
this._style(this._img, "transform", `translate(-${x * scale}px, -${y * scale}px)`);
}
_style(el, name, value) {
el.style.setProperty(name, value);
this._styleResets.push(() => el.style.removeProperty(name));
}
_resetStyles() {
for (const reset of this._styleResets)
reset();
this._styleResets = [];
}
render() {
const { crossorigin } = this._media.$store;
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$g);
$$_effect(() => $$_attr($$_root, "src", this._src()));
$$_effect(() => $$_attr($$_root, "crossorigin", crossorigin()));
$$_listen($$_root, "load", this._onLoaded.bind(this));
$$_ref($$_root, this._onImgRef.bind(this));
return $$_root;
})();
}
}
const $$_templ$f = /* @__PURE__ */ $$_create_template(`<!$><media-thumbnail part="thumbnail" mk-d></media-thumbnail>`);
class SliderThumbnail extends Component {
static el = defineElement({
tagName: "media-slider-thumbnail"
});
static register = [Thumbnail];
_media;
_slider;
constructor(instance) {
super(instance);
this._media = useMedia();
this._slider = useStore(SliderStoreFactory);
}
_getTime() {
const { duration } = this._media.$store;
return this._slider.pointerRate() * duration();
}
render() {
const time = this._getTime.bind(this);
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$f);
$$_effect(() => $$_attr($$_root, "time", time()));
$$_setup_custom_element($$_root);
return $$_root;
})();
}
}
const $$_templ$e = /* @__PURE__ */ $$_create_template(`<!$><video muted="" playsinline="" preload="auto" part="video" style="max-width: unset"></video>`);
class SliderVideo extends Component {
static el = defineElement({
tagName: "media-slider-video",
props: { src: void 0 }
});
_media;
_slider;
_video = null;
_canPlay = signal(false);
_error = signal(false);
_src;
_hidden;
onAttach() {
this._media = useMedia();
this._slider = useStore(SliderStoreFactory);
this._src = computed(this._getSrc.bind(this));
this._hidden = computed(this._isHidden.bind(this));
this.setAttributes({
"data-loading": this._isLoading.bind(this),
"aria-hidden": $ariaBool(this._hidden)
});
effect(this._onSrcChange.bind(this));
effect(this._onTimeUpdate.bind(this));
}
onConnect() {
if (this._video.readyState >= 2)
this._onCanPlay();
}
render() {
const { crossorigin } = this._media.$store;
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$e);
$$_effect(() => $$_attr($$_root, "src", this._src()));
$$_effect(() => $$_attr($$_root, "crossorigin", crossorigin()));
$$_listen($$_root, "canplay", this._onCanPlay.bind(this));
$$_listen($$_root, "error", this._onError.bind(this));
$$_ref($$_root, this._setVideo.bind(this));
return $$_root;
})();
}
_getSrc() {
const { canLoad } = this._media.$store;
return canLoad() ? this.$props.src() : null;
}
_isLoading() {
return !this._canPlay() && !this._hidden();
}
_isHidden() {
const { duration } = this._media.$store;
return !!this._error() || !this._canPlay() || !Number.isFinite(duration());
}
_onSrcChange() {
this._src();
this._canPlay.set(false);
this._error.set(false);
}
_onCanPlay(event) {
this._canPlay.set(true);
this.dispatch("can-play", { trigger: event });
}
_onError(event) {
this._error.set(true);
this.dispatch("error", { trigger: event });
}
_onTimeUpdate() {
const { duration } = this._media.$store;
const { pointerRate } = this._slider;
if (this._canPlay() && this._video && Number.isFinite(duration()) && Number.isFinite(pointerRate())) {
this._video.currentTime = pointerRate() * duration();
}
}
_setVideo(el) {
this._video = el;
}
}
function padNumberWithZeroes(num, expectedLength) {
const str = String(num);
const actualLength = str.length;
const shouldPad = actualLength < expectedLength;
if (shouldPad) {
const padLength = expectedLength - actualLength;
const padding = `0`.repeat(padLength);
return `${padding}${num}`;
}
return str;
}
function parseTime(duration) {
const hours = Math.trunc(duration / 3600);
const minutes = Math.trunc(duration % 3600 / 60);
const seconds = Math.trunc(duration % 60);
const fraction = Number((duration - Math.trunc(duration)).toPrecision(3));
return {
hours,
minutes,
seconds,
fraction
};
}
function formatTime(duration, shouldPadHours = false, shouldPadMinutes = false, shouldAlwaysShowHours = false) {
const { hours, minutes, seconds } = parseTime(duration);
const paddedHours = shouldPadHours ? padNumberWithZeroes(hours, 2) : hours;
const paddedMinutes = shouldPadMinutes ? padNumberWithZeroes(minutes, 2) : minutes;
const paddedSeconds = padNumberWithZeroes(seconds, 2);
if (hours > 0 || shouldAlwaysShowHours) {
return `${paddedHours}:${paddedMinutes}:${paddedSeconds}`;
}
return `${paddedMinutes}:${paddedSeconds}`;
}
function formatSpokenTime(duration) {
const spokenParts = [];
const { hours, minutes, seconds } = parseTime(duration);
if (hours > 0) {
spokenParts.push(`${hours} hour`);
}
if (minutes > 0) {
spokenParts.push(`${minutes} min`);
}
if (seconds > 0 || spokenParts.length === 0) {
spokenParts.push(`${seconds} sec`);
}
return spokenParts.join(" ");
}
const $$_templ$d = /* @__PURE__ */ $$_create_template(`<!$><span><!$></span>`);
class SliderValue extends Component {
static el = defineElement({
tagName: "media-slider-value",
props: {
type: "current",
format: void 0,
showHours: false,
padHours: false,
padMinutes: false,
decimalPlaces: 2
}
});
_format;
_text;
_slider;
onAttach() {
this._slider = useStore(SliderStoreFactory);
this._format = useContext(sliderValueFormatContext);
this._text = computed(this._getText.bind(this));
}
_getText() {
const { type, format, decimalPlaces, padHours, padMinutes, showHours } = this.$props;
const { value: sliderValue, pointerValue, min, max } = this._slider;
const value = type() === "current" ? sliderValue() : pointerValue();
if (format() === "percent") {
const range = max() - min();
const percent = value / range * 100;
return (this._format.percent ?? round)(percent, decimalPlaces()) + "\uFE6A";
} else if (format() === "time") {
return (this._format.time ?? formatTime)(value, padHours(), padMinutes(), showHours());
} else {
return this._format.value?.(value) ?? value.toFixed(2);
}
}
render() {
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$d), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, this._text);
return $$_root;
})();
}
}
class VolumeSlider extends Slider {
static el = defineElement({
tagName: "media-volume-slider",
props: {
...sliderProps,
min: defineProp({ value: 0, attribute: false }),
max: defineProp({ value: 100, attribute: false }),
value: defineProp({ value: 100, attribute: false })
},
store: SliderStoreFactory
});
_readonly = true;
onAttach(el) {
setAttributeIfEmpty(el, "aria-label", "Media volume");
super.onAttach(el);
{
canChangeVolume().then((canSet) => {
if (!canSet)
setAttribute(el, "aria-hidden", "true");
});
}
effect(this._watchVolume.bind(this));
}
_watchVolume() {
const { muted, volume } = this._media.$store;
const newValue = muted() ? 0 : volume() * 100;
this.$store.value.set(newValue);
this.dispatch("value-change", { detail: newValue });
}
_throttleVolumeChange = functionThrottle(this._onVolumeChange.bind(this), 25);
_onVolumeChange(event) {
if (!event.trigger)
return;
const mediaVolume = round(event.detail / 100, 3);
this._media.remote.changeVolume(mediaVolume, event);
}
_onValueChange(event) {
this._throttleVolumeChange(event);
}
_onDragValueChange(event) {
this._throttleVolumeChange(event);
}
_getARIAValueMin() {
return 0;
}
_getARIAValueMax() {
return 100;
}
}
class ClassManager {
_root;
_observer;
_map = /* @__PURE__ */ new Map();
_classes = signal([]);
constructor(el) {
this._root = el.firstChild;
this._observer = new MutationObserver(this._onMutation.bind(this));
this._observer.observe(this._root, { subtree: true, childList: true });
effect(this._watch.bind(this));
onDispose(this._destroy.bind(this));
}
_onMutation(records) {
const selector = Array.from(this._map.keys()).join(",");
for (const record of records) {
for (const node of record.addedNodes) {
if (isDOMElement(node) && node.matches(selector))
this._requestUpdate();
}
}
}
_observe(selector, $class) {
this._map.set(selector, $class);
this._classes.set((c) => [...c, $class]);
return this;
}
_requestUpdate = animationFrameThrottle(this._update.bind(this));
_update() {
for (const [selector, classes] of this._map) {
const _class = peek(classes);
for (const el of this._root.querySelectorAll(selector)) {
setAttribute(el, "class", _class);
}
}
}
_watch() {
for (const c of this._classes())
c();
this._requestUpdate();
}
_destroy() {
this._classes.set([]);
this._map.clear();
this._observer.disconnect();
}
}
const $$_templ$c = /* @__PURE__ */ $$_create_template(`<div part="chapters"></div>`), $$_templ_2$5 = /* @__PURE__ */ $$_create_template(`<div part="chapter-container"><div part="chapter"><div part="track"></div><div part="track track-fill" style="width: 0%"></div><div part="track track-progress" style="width: 0%"></div></div></div>`);
class SliderChaptersRenderer {
constructor(_media, _slider, _onChange) {
this._media = _media;
this._slider = _slider;
this._onChange = _onChange;
}
_chapters = [];
_refs = [];
_activeChapterIndex = 0;
_bufferedChapterIndex = 0;
render(cues, $class) {
return cues?.length ? $$_peek(
() => (() => {
const $$_root = $$_clone($$_templ$c);
$$_effect(() => $$_attr($$_root, "class", $class()));
$$_insert_lite($$_root, () => this._renderChapters(cues));
return $$_root;
})()
) : null;
}
_renderChapters(cues) {
this._chapters = this._fillChapterGaps(cues);
const firstChapter = this._chapters[0];
this._onChange(firstChapter.startTime === 0 ? firstChapter.text : "");
for (let i = 0; i < this._chapters.length; i++)
this._refs.push(this._renderChapter());
this._updateContainerWidths();
effect(this._watchFillPercent.bind(this));
effect(this._watchPointerPercent.bind(this));
effect(this._watchBufferedPercent.bind(this));
onDispose(() => {
this._refs = [];
this._activeChapterIndex = 0;
this._bufferedChapterIndex = 0;
});
return this._refs;
}
_renderChapter() {
return $$_clone($$_templ_2$5);
}
_getChapterTrackFill(container) {
return container.firstChild.firstChild.nextSibling;
}
_getChapterTrackProgress(container) {
return container.firstChild.lastChild;
}
_getChaptersEndTime() {
return this._chapters[this._chapters.length - 1].endTime;
}
_updateContainerWidths() {
let cue, endTime = this._getChaptersEndTime();
for (let i = 0; i < this._chapters.length; i++) {
cue = this._chapters[i];
this._refs[i].style.width = round((cue.endTime - cue.startTime) / endTime * 100, 3) + "%";
}
}
_watchFillPercent() {
let { fillPercent, value, pointing } = this._slider, currentChapter = this._chapters[this._activeChapterIndex], activeIndex = this._findActiveChapterIndex(
currentChapter.startTime <= peek(value) ? this._activeChapterIndex : 0,
fillPercent()
);
if (activeIndex > this._activeChapterIndex) {
this._updateTrackFillWidths(this._activeChapterIndex, activeIndex, "100%");
} else if (activeIndex < this._activeChapterIndex) {
this._updateTrackFillWidths(activeIndex + 1, this._activeChapterIndex + 1, "0%");
}
if (!peek(pointing) && this._activeChapterIndex !== activeIndex) {
this._onChange(this._chapters[activeIndex].text);
}
let trackFill = this._getChapterTrackFill(this._refs[activeIndex]), percent = this._calcChapterPercent(this._chapters[activeIndex], fillPercent()) + "%";
if (trackFill.style.width !== percent)
trackFill.style.width = percent;
this._activeChapterIndex = activeIndex;
}
_watchPointerPercent() {
let { pointing, pointerPercent } = this._slider;
if (!pointing())
return;
const activeIndex = this._findActiveChapterIndex(0, pointerPercent());
this._onChange(this._chapters[activeIndex].text);
}
_updateTrackFillWidths(start, end, width) {
for (let i = start; i < end; i++)
this._getChapterTrackFill(this._refs[i]).style.width = width;
}
_findActiveChapterIndex(startIndex, percent) {
let chapterPercent = 0;
for (let i = startIndex; i < this._chapters.length; i++) {
chapterPercent = this._calcChapterPercent(this._chapters[i], percent);
if (chapterPercent >= 0 && chapterPercent < 100)
return i;
}
return 0;
}
_watchBufferedPercent() {
this._updateBufferedPercent(this._bufferedPercent());
}
_updateBufferedPercent = animationFrameThrottle((bufferedPercent) => {
let width;
for (let i = this._bufferedChapterIndex; i < this._refs.length; i++) {
width = this._calcChapterPercent(this._chapters[i], bufferedPercent);
this._getChapterTrackProgress(this._refs[i]).style.width = width + "%";
if (width < 100) {
this._bufferedChapterIndex = i;
break;
}
}
});
_bufferedPercent = computed(this._calcMediaBufferedPercent.bind(this));
_calcMediaBufferedPercent() {
const { bufferedEnd, duration } = this._media;
return round(Math.min(bufferedEnd() / Math.max(duration(), 1), 1), 3) * 100;
}
_calcChapterPercent(cue, percent) {
const lastChapter = this._chapters[this._chapters.length - 1], startPercent = cue.startTime / lastChapter.endTime * 100, endPercent = cue.endTime / lastChapter.endTime * 100;
return Math.max(
0,
round(
percent >= endPercent ? 100 : (percent - startPercent) / (endPercent - startPercent) * 100,
3
)
);
}
_fillChapterGaps(cues) {
const chapters = [];
for (let i = 0; i < cues.length - 1; i++) {
const currentCue = cues[i], nextCue = cues[i + 1];
chapters.push(currentCue);
if (nextCue) {
const timeDiff = nextCue.startTime - currentCue.endTime;
if (timeDiff > 0) {
chapters.push(new window.VTTCue(currentCue.endTime, currentCue.endTime + timeDiff, ""));
}
}
}
chapters.push(cues[cues.length - 1]);
return chapters;
}
}
class TimeSlider extends Slider {
static el = defineElement({
tagName: "media-time-slider",
props: {
...sliderProps,
min: defineProp({ value: 0, attribute: false }),
max: defineProp({ value: 100, attribute: false }),
value: defineProp({ value: 0, attribute: false }),
pauseWhileDragging: false,
seekingRequestThrottle: 100,
chaptersClass: null,
chapterContainerClass: null,
chapterClass: null
},
store: SliderStoreFactory
});
_readonly = true;
_swipeGesture = true;
_dispatchSeeking;
_track = signal(null);
_chaptersRenderer;
_classManager;
constructor(instance) {
super(instance);
provideContext(sliderValueFormatContext, {
value: this._formatValue.bind(this),
time: this._formatTime.bind(this)
});
}
onAttach(el) {
setAttributeIfEmpty(el, "aria-label", "Media time");
super.onAttach(el);
this._chaptersRenderer = new SliderChaptersRenderer(
this._media.$store,
this.$store,
this._chapterTitle.set
);
this.setAttributes({
"data-chapters": this._hasChapters.bind(this)
});
this.setStyles({
"--media-buffered-percent": this._calcBufferedPercent.bind(this)
});
effect(this._watchCurrentTime.bind(this));
effect(this._watchSeekingThrottle.bind(this));
effect(this._onTrackChange.bind(this));
scopedRaf(() => {
effect(this._watchPreviewing.bind(this));
});
}
_calcBufferedPercent() {
const { bufferedEnd, duration } = this._media.$store;
return round(Math.min(bufferedEnd() / Math.max(duration(), 1), 1) * 100, 3) + "%";
}
_hasChapters() {
const { duration } = this._media.$store;
this._classManager?._requestUpdate();
return this._track()?.cues.length && Number.isFinite(duration()) && duration() > 0;
}
onConnect(el) {
super.onConnect(el);
this._onTrackModeChange();
listenEvent(this._media.textTracks, "mode-change", this._onTrackModeChange.bind(this));
const { chapterContainerClass, chapterClass, trackClass, trackFillClass, trackProgressClass } = this.$props;
this._classManager = new ClassManager(el)._observe('[part="chapter-container"]', chapterContainerClass)._observe('[part="chapter"]', chapterClass)._observe('[part="track"]', trackClass)._observe('[part~="track-fill"]', trackFillClass)._observe('[part~="track-progress"]', trackProgressClass);
}
render() {
const tracks = super.render(), { chaptersClass } = this.$props;
return [$$_computed(() => this._chaptersRenderer.render(this._track()?.cues, chaptersClass)), tracks];
}
_watchSeekingThrottle() {
this._dispatchSeeking = functionThrottle(
this._seeking.bind(this),
this.$props.seekingRequestThrottle()
);
}
_watchCurrentTime() {
const { currentTime } = this._media.$store, { value, dragging } = this.$store, newValue = this._timeToPercent(currentTime());
if (!peek(dragging)) {
value.set(newValue);
this.dispatch("value-change", { detail: newValue });
}
}
_watchPreviewing() {
const player = this._media.player;
player && this._preview && setAttribute(player, "data-preview", this.$store.interactive());
}
_seeking(time, event) {
this._media.remote.seeking(time, event);
}
_seek(time, percent, event) {
this._dispatchSeeking.cancel();
const { live } = this._media.$store;
if (live() && percent >= 99) {
this._media.remote.seekToLiveEdge(event);
return;
}
this._media.remote.seek(time, event);
}
_playingBeforeDragStart = false;
_onDragStart(event) {
const { pauseWhileDragging } = this.$props;
if (pauseWhileDragging()) {
const { paused } = this._media.$store;
this._playingBeforeDragStart = !paused();
this._media.remote.pause(event);
}
}
_onDragValueChange(event) {
this._dispatchSeeking(this._percentToTime(event.detail), event);
}
_onDragEnd(event) {
const percent = event.detail;
this._seek(this._percentToTime(percent), percent, event);
const { pauseWhileDragging } = this.$props;
if (pauseWhileDragging() && this._playingBeforeDragStart) {
this._media.remote.play(event);
this._playingBeforeDragStart = false;
}
}
_onValueChange(event) {
const { dragging } = this.$store;
if (dragging() || !event.trigger)
return;
this._onDragEnd(event);
}
// -------------------------------------------------------------------------------------------
// Props
// -------------------------------------------------------------------------------------------
_getStep() {
const value = this.$props.step() / this._media.$store.duration() * 100;
return Number.isFinite(value) ? value : 1;
}
_getKeyStep() {
const value = this.$props.keyStep() / this._media.$store.duration() * 100;
return Number.isFinite(value) ? value : 1;
}
_roundValue(value) {
return round(value, 3);
}
_isDisabled() {
const { canSeek } = this._media.$store;
return super._isDisabled() || !canSeek();
}
// -------------------------------------------------------------------------------------------
// ARIA
// -------------------------------------------------------------------------------------------
_getARIAValueMin() {
return 0;
}
_getARIAValueMax() {
return 100;
}
_getARIAValueText() {
const time = this._percentToTime(this.$store.value()), { duration } = this._media.$store;
return Number.isFinite(time) ? `${formatSpokenTime(time)} out of ${formatSpokenTime(duration())}` : "live";
}
// -------------------------------------------------------------------------------------------
// Format
// -------------------------------------------------------------------------------------------
_percentToTime(percent) {
const { duration } = this._media.$store;
return Math.round(percent / 100 * duration());
}
_timeToPercent(time) {
const { liveEdge, duration } = this._media.$store, rate = Math.max(0, Math.min(1, liveEdge() ? 1 : Math.min(time, duration()) / duration()));
return Number.isNaN(rate) ? 0 : Number.isFinite(rate) ? rate * 100 : 100;
}
_formatValue(percent) {
const time = this._percentToTime(percent), { live, duration } = this._media.$store;
return Number.isFinite(time) ? (live() ? time - duration() : time).toFixed(0) : "LIVE";
}
_formatTime(percent, padHours, padMinutes, showHours) {
const time = this._percentToTime(percent), { live, duration } = this._media.$store, value = live() ? time - duration() : time;
return Number.isFinite(time) ? `${value < 0 ? "-" : ""}${formatTime(
Math.abs(value),
padHours,
isNull(padMinutes) ? Math.abs(value) >= 3600 : padMinutes,
showHours
)}` : "LIVE";
}
// -------------------------------------------------------------------------------------------
// Chapters
// -------------------------------------------------------------------------------------------
_chapterTitleEl = null;
_chapterTitle = signal("");
_onTrackModeChange() {
onTrackChapterChange(this._media.textTracks, peek(this._track), this._track.set);
}
_onTrackChange() {
this._track();
this._chapterTitleEl = this.el?.querySelector('[part="chapter-title"]') ?? null;
if (!this._chapterTitleEl)
return;
effect(this._onChapterTitleChange.bind(this));
return () => {
this._chapterTitleEl.textContent = "";
this._chapterTitleEl = null;
};
}
_onChapterTitleChange() {
this._chapterTitleEl.textContent = this._chapterTitle();
}
}
const menuContext = createContext();
const FOCUSABLE_ELEMENTS_SELECTOR = /* @__PURE__ */ [
"a[href]",
"[tabindex]",
"input",
"select",
"button"
].map((selector) => `${selector}:not([aria-hidden])`).join(",");
const VALID_KEYS = /* @__PURE__ */ new Set([
"Escape",
"Tab",
"ArrowUp",
"ArrowDown",
"Home",
"PageUp",
"End",
"PageDown",
"Enter",
" "
]);
class MenuFocusController {
constructor(_delegate) {
this._delegate = _delegate;
}
_index = 0;
_el = null;
_elements = [];
get _items() {
return this._elements;
}
_attach(el) {
listenEvent(el, "focus", this._onFocus.bind(this));
this._el = el;
}
_listen() {
if (!this._el)
return;
this._update();
listenEvent(this._el, "keyup", this._onKeyUp.bind(this));
listenEvent(this._el, "keydown", this._onKeyDown.bind(this));
onDispose(() => {
this._index = 0;
this._elements = [];
});
}
_update() {
this._index = 0;
this._elements = this._getFocusableElements();
}
_scroll(index = this._findActiveIndex()) {
const element = this._elements[index], container = this._delegate._getScrollContainer();
if (element && container) {
requestAnimationFrame(() => {
container.scrollTop = element.offsetTop - container.offsetHeight / 2 + element.offsetHeight / 2;
});
}
}
_focusAt(index) {
this._index = index;
this._elements[index]?.focus();
this._scroll(index);
}
_findActiveIndex() {
return this._elements.findIndex((el) => el.getAttribute("aria-checked") === "true");
}
_onFocus() {
setTimeout(() => {
const index = this._findActiveIndex();
this._focusAt(index >= 0 ? index : 0);
}, 100);
}
_onKeyUp(event) {
if (!VALID_KEYS.has(event.key))
return;
event.stopPropagation();
event.preventDefault();
}
_onKeyDown(event) {
if (!VALID_KEYS.has(event.key))
return;
event.stopPropagation();
event.preventDefault();
switch (event.key) {
case "Escape":
this._delegate._closeMenu(event);
break;
case "Tab":
this._focusAt(this._nextIndex(event.shiftKey ? -1 : 1));
break;
case "ArrowUp":
this._focusAt(this._nextIndex(-1));
break;
case "ArrowDown":
this._focusAt(this._nextIndex(1));
break;
case "Home":
case "PageUp":
this._focusAt(0);
break;
case "End":
case "PageDown":
this._focusAt(this._elements.length - 1);
break;
}
}
_nextIndex(delta) {
let index = this._index;
do {
index = (index + delta + this._elements.length) % this._elements.length;
} while (this._elements[index].offsetParent === null);
return index;
}
_getFocusableElements() {
if (!this._el)
return [];
const focusableElements = this._el.querySelectorAll(FOCUSABLE_ELEMENTS_SELECTOR), elements = [];
const is = (node) => node.hasAttribute("data-media-menu-items");
for (const el of focusableElements) {
if (el instanceof HTMLElement && el.offsetParent !== null && // does not have display: none
isElementParent(this._el, el, is)) {
elements.push(el);
}
}
return elements;
}
}
var __defProp$2 = Object.defineProperty;
var __getOwnPropDesc$2 = Object.getOwnPropertyDescriptor;
var __decorateClass$2 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$2(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$2(target, key, result);
return result;
};
let idCount = 0;
class Menu extends Component {
static el = defineElement({
tagName: "media-menu",
props: { position: null }
});
_media;
_menuId;
_menuButtonId;
_expanded = signal(false);
_disabled = signal(false);
_buttonDisabled = signal(false);
_parentMenu;
_submenus = /* @__PURE__ */ new Set();
_menuButton = null;
_menuItems = null;
_menuObserver = null;
_focus;
constructor(instance) {
super(instance);
this._media = useMedia();
const currentIdCount = ++idCount;
this._menuId = `media-menu-${currentIdCount}`;
this._menuButtonId = `media-menu-button-${currentIdCount}`;
if (hasProvidedContext(menuContext)) {
this._parentMenu = useContext(menuContext);
}
this._focus = new MenuFocusController({
_getScrollContainer: this._findScrollContainer.bind(this),
_closeMenu: this.close.bind(this)
});
provideContext(menuContext, {
_expanded: this._expanded,
_hint: signal(""),
_disable: this._disable.bind(this),
_attachMenuButton: this._attachMenuButton.bind(this),
_attachMenuItems: this._attachMenuItems.bind(this),
_attachObserver: this._attachObserver.bind(this),
_disableMenuButton: this._disableMenuButton.bind(this),
_addSubmenu: this._addSubmenu.bind(this)
});
}
onAttach(el) {
const { position } = this.$props;
this.setAttributes({
position,
"data-open": this._expanded,
"data-submenu": !!this._parentMenu,
"data-disabled": this._isDisabled.bind(this),
"data-media-menu": true
});
}
onConnect(el) {
if (!this._parentMenu) {
effect(this._watchBreakpoint.bind(this));
}
effect(this._watchExpanded.bind(this));
this._parentMenu?._addSubmenu(el);
requestAnimationFrame(() => this._onResize());
}
onDestroy() {
this._removePopupMenu();
this._menuButton = null;
this._menuItems = null;
this._menuObserver = null;
}
_removePopupMenu() {
if (!this._menuItems || this.el?.contains(this._menuItems))
return;
const menu = this._menuItems?.parentElement;
this.el.append(this._menuItems);
if (menu?.localName === "media-menu") {
menu.destroy();
menu.remove();
}
}
_watchBreakpoint() {
if (!this.el)
return;
const { breakpointX, breakpointY, viewType, orientation, fullscreen } = this._media.$store, popup = viewType() === "audio" ? breakpointX() === "sm" : breakpointY() === "sm";
if (!this._menuItems || this._parentMenu)
return;
setAttribute(this.el, "data-popup", popup);
setAttribute(this.el, "data-popup-wide", popup && orientation() === "landscape");
if (popup && !fullscreen()) {
if (this.el.contains?.(this._menuItems)) {
const menu = this.el.cloneNode();
menu.appendChild(this._menuItems);
requestAnimationFrame(() => {
if (!this.el)
return;
const mediaRing = "--media-focus-ring", mediaRingValue = getComputedStyle(this.el).getPropertyValue(mediaRing);
if (mediaRingValue)
setStyle(menu, mediaRing, mediaRingValue);
});
scoped(() => {
document.body.append(menu);
}, this._media.scope);
}
}
this._onResize();
return () => this._removePopupMenu();
}
_watchExpanded() {
const expanded = this._isExpanded();
this._onResize();
this._updateMenuItemsHidden(expanded);
if (!expanded)
return;
this._focus._listen();
const closeTarget = this._getCloseTarget();
if (closeTarget) {
onPress(closeTarget, this._onCloseTargetPress.bind(this));
}
this.listen("pointerup", this._onPointerUp.bind(this));
listenEvent(window, "pointerup", this._onWindowPointerUp.bind(this));
}
_attachMenuButton(el) {
const isMenuItem = !!this._parentMenu, isExpanded = this._isExpanded.bind(this), isARIAExpanded = $ariaBool(isExpanded), isARIADisabled = $ariaBool(this._isDisabled.bind(this));
setAttributeIfEmpty(el, "tabindex", isMenuItem ? "-1" : "0");
setAttributeIfEmpty(el, "role", isMenuItem ? "menuitem" : "button");
setAttribute(el, "id", this._menuButtonId);
setAttribute(el, "aria-controls", this._menuId);
setAttribute(el, "aria-haspopup", "true");
effect(() => {
setAttribute(el, "aria-disabled", isARIADisabled());
setAttribute(el, "aria-expanded", isARIAExpanded());
if (!isMenuItem)
setAttribute(el, "aria-pressed", isARIAExpanded());
setAttribute(el, "data-pressed", isExpanded());
});
setAttribute(el, "data-media-button", !isMenuItem);
setAttribute(el, "data-media-menu-button", "");
onPress(el, this._onMenuButtonPress.bind(this));
this._menuButton = el;
}
_attachMenuItems(el) {
setAttributeIfEmpty(el, "role", "menu");
setAttributeIfEmpty(el, "tabindex", "-1");
setAttribute(el, "id", this._menuId);
setAttribute(el, "aria-describedby", this._menuButtonId);
setAttribute(el, "data-media-menu-items", "");
this._menuItems = el;
this._focus._attach(el);
this._watchBreakpoint();
this._updateMenuItemsHidden(peek(this._expanded));
}
_attachObserver(observer) {
this._menuObserver = observer;
}
_updateMenuItemsHidden(expanded) {
if (this._menuItems)
setAttribute(this._menuItems, "aria-hidden", ariaBool$1(!expanded));
}
_disableMenuButton(disabled) {
this._buttonDisabled.set(disabled);
}
_onMenuButtonPress(event) {
if (this._parentMenu)
event.stopPropagation();
if (this._isDisabled())
return;
this._expanded.set((expanded) => !expanded);
this._changeIdleTracking();
tick();
if (isKeyboardEvent(event)) {
this._menuItems?.focus();
}
this._onChange(event);
}
_onChange(trigger) {
const expanded = peek(this._expanded);
this.dispatch(expanded ? "open" : "close", { trigger });
if (expanded) {
if (!this._parentMenu) {
this._media.activeMenu?.close(trigger);
this._media.activeMenu = this;
}
this._menuObserver?._onOpen?.(trigger);
} else {
if (!this._parentMenu) {
setTimeout(() => {
for (const el of this._submenus)
el.close(trigger);
}, 300);
this._media.activeMenu = null;
}
this._menuObserver?._onClose?.(trigger);
}
if (expanded && !isKeyboardEvent(trigger)) {
requestAnimationFrame(() => {
this._focus._update();
setTimeout(() => {
this._focus._scroll();
}, 100);
});
}
}
_isExpanded() {
return !this._isDisabled() && this._expanded();
}
_isDisabled() {
return this._disabled() || this._buttonDisabled();
}
_disableBind = this._disable.bind(this);
_disable(disabled) {
this._disabled.set(disabled);
}
_onPointerUp(event) {
event.stopPropagation();
}
_onWindowPointerUp() {
if (this._parentMenu)
return setTimeout(this.close.bind(this), 800);
else
this.close();
}
_onCloseTargetPress(event) {
event.stopPropagation();
this.close(event);
}
_getCloseTarget() {
const target = this.el.querySelector('[slot="close-target"]');
return isElementParent(this.el, target) ? target : null;
}
_findScrollContainer() {
if (!this._parentMenu) {
return this._menuItems;
} else {
let el = this.el;
while (el && el.tagName !== "media-menu" && el.hasAttribute("data-submenu")) {
el = el.parentNode;
}
return el;
}
}
_changeIdleTracking(trigger) {
if (this._parentMenu)
return;
if (this._expanded())
this._media.remote.pauseUserIdle(trigger);
else
this._media.remote.resumeUserIdle(trigger);
}
_addSubmenu(el) {
this._submenus.add(el);
listenEvent(el, "open", this._onSubmenuOpenBind);
listenEvent(el, "close", this._onSubmenuCloseBind);
onDispose(this._removeSubmenuBind);
}
_removeSubmenuBind = this._removeSubmenu.bind(this);
_removeSubmenu(el) {
this._submenus.delete(el);
}
_onSubmenuOpenBind = this._onSubmenuOpen.bind(this);
_onSubmenuOpen(event) {
for (const el of this._submenus) {
if (el !== event.target)
el.setAttribute("aria-hidden", "true");
}
this._onResize();
}
_onSubmenuCloseBind = this._onSubmenuClose.bind(this);
_onSubmenuClose() {
for (const el of this._submenus)
el.removeAttribute("aria-hidden");
this._onResize();
}
_onResize() {
if (!this._menuItems || false)
return;
let style = getComputedStyle(this._menuItems), height = parseFloat(style.paddingTop) + parseFloat(style.paddingBottom);
let children = [...this._menuItems.children];
if (children[0]?.localName === "shadow-root") {
children.push(...children[0].children);
}
for (const child of children) {
height += child.offsetHeight;
}
requestAnimationFrame(() => {
if (!this._menuItems)
return;
setAttribute(this._menuItems, "data-resizing", "");
setTimeout(() => {
if (!this._menuItems)
return;
setAttribute(this._menuItems, "data-resizing", false);
}, 250);
setStyle(this._menuItems, "--menu-height", height + "px");
});
}
open(trigger) {
if (peek(this._expanded))
return;
this._expanded.set(true);
tick();
this._onChange(trigger);
if (isKeyboardEvent(trigger)) {
this._menuItems?.focus();
}
this._changeIdleTracking(trigger);
}
close(trigger) {
if (!peek(this._expanded))
return;
this._expanded.set(false);
tick();
if (isKeyboardEvent(trigger)) {
requestAnimationFrame(() => {
this._menuButton?.focus();
});
}
this._onChange(trigger);
this._changeIdleTracking(trigger);
}
}
__decorateClass$2([
method
], Menu.prototype, "open", 1);
__decorateClass$2([
method
], Menu.prototype, "close", 1);
class MenuButton extends Component {
static el = defineElement({
tagName: "media-menu-button",
props: { disabled: false }
});
_menu;
constructor(instance) {
super(instance);
this._menu = useContext(menuContext);
new FocusVisibleController(instance);
new TooltipController(instance);
}
onAttach(el) {
this._menu._attachMenuButton(el);
effect(this._watchDisabled.bind(this));
}
onConnect(el) {
const hint = Array.from(el.querySelectorAll('[slot="hint"]')).pop();
if (hint) {
effect(() => {
const text = this._menu._hint();
if (text)
hint.textContent = text;
});
}
}
_watchDisabled() {
this._menu._disableMenuButton(this.$props.disabled());
}
}
class MenuItems extends Component {
static el = defineElement({
tagName: "media-menu-items"
});
_menu;
constructor(instance) {
super(instance);
this._menu = useContext(menuContext);
new FocusVisibleController(instance);
}
onAttach(el) {
this._menu._attachMenuItems(el);
}
}
const radioGroupContext = createContext();
var __defProp$1 = Object.defineProperty;
var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
var __decorateClass$1 = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc$1(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp$1(target, key, result);
return result;
};
class RadioGroup extends Component {
static el = defineElement({
tagName: "media-radio-group",
props: { value: "" }
});
_group = /* @__PURE__ */ new Set();
_value = signal("");
get values() {
return Array.from(this._group).map((radio) => radio._value());
}
get value() {
return this._value();
}
set value(value) {
this._onChange(value);
}
constructor(instance) {
super(instance);
provideContext(radioGroupContext, {
add: this._addRadio.bind(this),
remove: this._removeRadio.bind(this)
});
}
onAttach(el) {
const isMenuItem = hasProvidedContext(menuContext);
if (!isMenuItem)
setAttributeIfEmpty(el, "role", "radiogroup");
this._watchValueProp();
this.setAttributes({ value: this._value });
}
onConnect() {
effect(this._watchValueProp.bind(this));
}
onDestroy() {
this._group.clear();
}
_addRadio(radio) {
if (this._group.has(radio))
return;
this._group.add(radio);
radio._onCheck = this._onChangeBind;
radio._check(radio._value() === this._value());
}
_removeRadio(radio) {
radio._onCheck = null;
this._group.delete(radio);
}
_watchValueProp() {
this._onChange(this.$props.value());
}
_onChangeBind = this._onChange.bind(this);
_onChange(newValue, trigger) {
const currentValue = peek(this._value);
if (!newValue || newValue === currentValue)
return;
const currentRadio = this._findRadio(currentValue), newRadio = this._findRadio(newValue);
currentRadio?._check(false, trigger);
newRadio?._check(true, trigger);
this._value.set(newValue);
this.dispatch("change", { trigger });
}
_findRadio(newValue) {
for (const radio of this._group) {
if (newValue === peek(radio._value))
return radio;
}
return null;
}
}
__decorateClass$1([
prop
], RadioGroup.prototype, "values", 1);
__decorateClass$1([
prop
], RadioGroup.prototype, "value", 1);
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
for (var i = decorators.length - 1, decorator; i >= 0; i--)
if (decorator = decorators[i])
result = (kind ? decorator(target, key, result) : decorator(result)) || result;
if (kind && result)
__defProp(target, key, result);
return result;
};
const $$_templ$b = /* @__PURE__ */ $$_create_template(`<!$><div part="check"></div>`);
class Radio extends Component {
static el = defineElement({
tagName: "media-radio",
props: { value: "" }
});
_checked = signal(false);
_controller = {
_value: this.$props.value,
_check: this._check.bind(this),
_onCheck: null
};
get checked() {
return this._checked();
}
constructor(instance) {
super(instance);
new FocusVisibleController(instance);
}
onAttach() {
effect(this._watchValue.bind(this));
this._addToGroup();
const isMenuItem = hasProvidedContext(menuContext);
this.setAttributes({
value: this.$props.value,
role: isMenuItem ? "menuitemradio" : "radio",
tabindex: isMenuItem ? -1 : 0,
"aria-checked": $ariaBool(this._checked)
});
}
onConnect(el) {
this._addToGroup();
onPress(el, this._onPress.bind(this));
}
onDisconnect() {
const group = useContext(radioGroupContext);
group.remove(this._controller);
}
_addToGroup() {
const group = useContext(radioGroupContext);
group.add(this._controller);
}
_watchValue() {
const { value } = this.$props, newValue = value();
if (peek(this._checked)) {
this._controller._onCheck?.(newValue);
}
}
_onPress(event) {
if (peek(this._checked))
return;
this._checked.set(true);
this.dispatch("change", { trigger: event });
this._controller._onCheck?.(peek(this.$props.value), event);
}
_check(value, trigger) {
if (peek(this._checked) === value)
return;
this._checked.set(value);
this.dispatch("change", { trigger });
}
render() {
return $$_next_template($$_templ$b);
}
}
__decorateClass([
prop
], Radio.prototype, "checked", 1);
const $$_templ$a = /* @__PURE__ */ $$_create_template(`<!$><media-radio-group mk-d><!$></media-radio-group>`), $$_templ_2$4 = /* @__PURE__ */ $$_create_template(`<!$><media-radio mk-d><!$></media-radio>`);
function renderRadioGroup(props) {
const { value, onChange, radioGroupClass } = props;
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$a), $$_expr = $$_walker.nextNode();
$$_effect(() => $$_attr($$_root, "class", radioGroupClass()));
$$_listen($$_root, "change", onChange);
$$_scoped(() => {
$$_insert_at_marker_lite($$_expr, () => renderOptions(props));
}, $$_setup_custom_element($$_root, { value }));
return $$_root;
})();
}
function renderOptions(props) {
const { options } = props;
return options().map((option) => {
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_2$4), $$_expr = $$_walker.nextNode();
$$_attr($$_root, "part", props.part);
$$_scoped(() => {
$$_insert_at_marker_lite($$_expr, option.content);
}, $$_setup_custom_element($$_root, { value: option.value }));
return $$_root;
})();
});
}
const $$_templ$9 = /* @__PURE__ */ $$_create_template(`<!$><media-thumbnail part="thumbnail" mk-d></media-thumbnail>`), $$_templ_2$3 = /* @__PURE__ */ $$_create_template(`<!$><div part="content"><div part="title"><!$></div><div part="start-time"><!$></div><div part="duration"><!$></div></div>`);
class ChaptersMenuItems extends MenuItems {
static el = defineElement({
tagName: "media-chapters-menu-items",
props: {
containerClass: null,
chapterClass: null,
thumbnailClass: null,
contentClass: null,
titleClass: null,
startTimeClass: null,
durationClass: null
}
});
static register = [Thumbnail, RadioGroup, Radio];
_media;
_index = signal(0);
_track = signal(null);
constructor(instance) {
super(instance);
this._media = useMedia();
}
onAttach(el) {
super.onAttach(el);
this._menu._attachObserver({
_onOpen: this._onOpen.bind(this)
});
this.setAttributes({
"data-thumbnails": this._hasThumbnails.bind(this)
});
}
_onOpen() {
peek(() => this._watchCurrentTime());
}
onConnect(el) {
effect(this._watchCurrentTime.bind(this));
effect(this._watchControllerDisabled.bind(this));
this._onTrackModeChange();
listenEvent(this._media.textTracks, "mode-change", this._onTrackModeChange.bind(this));
const {
chapterClass,
thumbnailClass,
contentClass,
titleClass,
startTimeClass,
durationClass
} = this.$props;
new ClassManager(el)._observe('[part="chapter"]', chapterClass)._observe('[part="thumbnail"]', thumbnailClass)._observe('[part="content"]', contentClass)._observe('[part="title"]', titleClass)._observe('[part="start-time"]', startTimeClass)._observe('[part="duration"]', durationClass);
}
_hasThumbnails() {
const { thumbnailCues } = this._media.$store;
return thumbnailCues().length > 0;
}
_watchCurrentTime() {
if (!this._menu._expanded())
return;
const track = this._track();
if (!track) {
this._index.set(-1);
return;
}
const { currentTime } = this._media.$store, time = currentTime(), activeCueIndex = track.cues.findIndex((cue) => isCueActive(cue, time));
this._index.set(activeCueIndex);
if (activeCueIndex >= 0) {
const cue = track.cues[activeCueIndex], radio = this.el.querySelector(`shadow-root media-radio[aria-checked='true']`), playedPercent = (time - cue.startTime) / (cue.endTime - cue.startTime) * 100;
radio && setStyle(radio, "--played-percent", round(playedPercent, 3) + "%");
}
}
_watchControllerDisabled() {
this._menu._disable(this._isDisabled());
}
_isDisabled() {
const track = this._track();
return !track || !track.cues.length;
}
_onChange(event) {
if (this._isDisabled() || !event.trigger)
return;
const index = +event.target.value, cues = this._track()?.cues;
if (isNumber(index) && cues?.[index]) {
this._index.set(index);
this._media.remote.seek(cues[index].startTime, event);
}
}
_onTrackModeChange() {
onTrackChapterChange(this._media.textTracks, peek(this._track), this._track.set);
}
_getValue() {
return this._index() + "";
}
_getOptions() {
const track = this._track();
if (!track)
return [];
return track.cues.map((cue, i) => ({
value: i + "",
content: () => [
(() => {
const $$_signal = $$_computed(
() => this._hasThumbnails() && (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$9);
$$_setup_custom_element($$_root, { time: cue.startTime });
return $$_root;
})()
);
$$_signal();
return $$_signal;
})(),
(() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_2$3), $$_expr = $$_walker.nextNode(), $$_expr_2 = $$_walker.nextNode(), $$_expr_3 = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, cue.text);
$$_insert_at_marker_lite($$_expr_2, () => formatTime(cue.startTime, false, cue.startTime >= 3600));
$$_insert_at_marker_lite($$_expr_3, () => formatSpokenTime(cue.endTime - cue.startTime));
return $$_root;
})()
]
}));
}
render() {
const { containerClass } = this.$props;
return renderRadioGroup({
part: "chapter",
value: this._getValue.bind(this),
options: this._getOptions.bind(this),
radioGroupClass: containerClass,
onChange: this._onChange.bind(this)
});
}
}
const $$_templ$8 = /* @__PURE__ */ $$_create_template(`<!$><span part="label"><!$></span>`);
class AudioMenuItems extends MenuItems {
static el = defineElement({
tagName: "media-audio-menu-items",
props: {
emptyLabel: "Default",
radioClass: null,
radioGroupClass: null,
radioCheckClass: null
}
});
static register = [RadioGroup, Radio];
_media;
constructor(instance) {
super(instance);
this._media = useMedia();
}
onConnect(el) {
effect(this._watchControllerDisabled.bind(this));
effect(this._watchHintText.bind(this));
const { radioClass, radioCheckClass } = this.$props;
new ClassManager(el)._observe("media-radio", radioClass)._observe('[part="check"]', radioCheckClass);
}
_watchHintText() {
const { emptyLabel } = this.$props, { audioTrack } = this._media.$store, track = audioTrack();
this._menu._hint.set(track?.label ?? emptyLabel());
}
_watchControllerDisabled() {
this._menu._disable(this._isDisabled());
}
_isDisabled() {
const { audioTracks } = this._media.$store;
return audioTracks().length === 0;
}
_onChange(event) {
if (this._isDisabled())
return;
const radioGroup = event.target;
const value = radioGroup.value, index = this._media.audioTracks.toArray().findIndex((track) => track.label.toLowerCase() === value);
if (index >= 0)
this._media.remote.changeAudioTrack(index, event);
}
_getValue() {
const { audioTrack } = this._media.$store;
const track = audioTrack();
return track ? track.label.toLowerCase() : "";
}
_getOptions() {
const { audioTracks } = this._media.$store;
return audioTracks().map((track) => ({
value: track.label.toLowerCase(),
content: () => (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$8), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, track.label);
return $$_root;
})()
}));
}
render() {
const { radioGroupClass } = this.$props;
return renderRadioGroup({
value: this._getValue.bind(this),
options: this._getOptions.bind(this),
radioGroupClass,
onChange: this._onChange.bind(this)
});
}
}
var Icon$51 = `<path fill-rule="evenodd" clip-rule="evenodd" d="M26.6667 5.99998C26.6667 5.63179 26.3682 5.33331 26 5.33331H11.3333C10.9651 5.33331 10.6667 5.63179 10.6667 5.99998V17.5714C10.6667 17.6694 10.5644 17.7342 10.4741 17.6962C9.91823 17.4625 9.30754 17.3333 8.66667 17.3333C6.08934 17.3333 4 19.4226 4 22C4 24.5773 6.08934 26.6666 8.66667 26.6666C11.244 26.6666 13.3333 24.5773 13.3333 22V8.66665C13.3333 8.29846 13.6318 7.99998 14 7.99998L23.3333 7.99998C23.7015 7.99998 24 8.29846 24 8.66665V14.9048C24 15.0027 23.8978 15.0675 23.8075 15.0296C23.2516 14.7958 22.6409 14.6666 22 14.6666C19.4227 14.6666 17.3333 16.756 17.3333 19.3333C17.3333 21.9106 19.4227 24 22 24C24.5773 24 26.6667 21.9106 26.6667 19.3333V5.99998ZM22 21.3333C23.1046 21.3333 24 20.4379 24 19.3333C24 18.2287 23.1046 17.3333 22 17.3333C20.8954 17.3333 20 18.2287 20 19.3333C20 20.4379 20.8954 21.3333 22 21.3333ZM8.66667 24C9.77124 24 10.6667 23.1045 10.6667 22C10.6667 20.8954 9.77124 20 8.66667 20C7.5621 20 6.66667 20.8954 6.66667 22C6.66667 23.1045 7.5621 24 8.66667 24Z" fill="currentColor"/>`;
var Icon$10 = `<path d="M13.0908 14.3334C12.972 14.3334 12.9125 14.1898 12.9965 14.1058L17.7021 9.40022C17.9625 9.13987 17.9625 8.71776 17.7021 8.45741L16.2879 7.04319C16.0275 6.78284 15.6054 6.78284 15.3451 7.04319L6.8598 15.5285C6.59945 15.7888 6.59945 16.2109 6.8598 16.4713L8.27401 17.8855L8.27536 17.8868L15.3453 24.9568C15.6057 25.2172 16.0278 25.2172 16.2881 24.9568L17.7024 23.5426C17.9627 23.2822 17.9627 22.8601 17.7024 22.5998L12.9969 17.8944C12.9129 17.8104 12.9724 17.6668 13.0912 17.6668L26 17.6668C26.3682 17.6668 26.6667 17.3683 26.6667 17.0001V15.0001C26.6667 14.6319 26.3682 14.3334 26 14.3334L13.0908 14.3334Z" fill="currentColor"/>`;
var Icon$18 = `<path d="M15.905 17.4809C15.9571 17.533 16.0415 17.533 16.0936 17.4809L22.4111 11.1635C22.6714 10.9031 23.0935 10.9031 23.3539 11.1635L24.9567 12.7662C25.217 13.0266 25.217 13.4487 24.9567 13.709L18.1028 20.5629C18.0937 20.5732 18.0842 20.5833 18.0744 20.5931L16.4716 22.1959C16.2113 22.4562 15.7892 22.4562 15.5288 22.1959L7.04353 13.7106C6.78318 13.4503 6.78318 13.0281 7.04353 12.7678L8.6463 11.165C8.90665 10.9047 9.32876 10.9047 9.58911 11.165L15.905 17.4809Z" fill="currentColor"/>`;
const $$_templ$7 = /* @__PURE__ */ $$_create_template(`<!$><span slot="label"><!$></span>`), $$_templ_2$2 = /* @__PURE__ */ $$_create_template(`<!$><div slot="hint"></div>`);
function renderMenuButtonContent({ label, iconPaths }) {
return [
$$_create_component(Icon, { slot: "close-icon", paths: Icon$10 }),
$$_create_component(Icon, { slot: "icon", paths: iconPaths }),
(() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$7), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, label);
return $$_root;
})(),
$$_next_template($$_templ_2$2),
$$_create_component(Icon, { slot: "open-icon", paths: Icon$18 })
];
}
class AudioMenuButton extends MenuButton {
static el = defineElement({
tagName: "media-audio-menu-button",
props: { disabled: false, label: "Audio" }
});
render() {
const { label } = this.$props;
return renderMenuButtonContent({
label,
iconPaths: Icon$51
});
}
}
const $$_templ$6 = /* @__PURE__ */ $$_create_template(`<!$><span part="label"><!$></span>`), $$_templ_2$1 = $$_templ$6;
class CaptionsMenuItems extends MenuItems {
static el = defineElement({
tagName: "media-captions-menu-items",
props: {
offLabel: "Off",
radioClass: null,
radioGroupClass: null,
radioCheckClass: null
}
});
static register = [RadioGroup, Radio];
_media;
constructor(instance) {
super(instance);
this._media = useMedia();
}
onConnect(el) {
effect(this._watchControllerDisabled.bind(this));
effect(this._watchHintText.bind(this));
const { radioClass, radioCheckClass } = this.$props;
new ClassManager(el)._observe("media-radio", radioClass)._observe('[part="check"]', radioCheckClass);
}
_watchHintText() {
const { offLabel } = this.$props, { textTrack } = this._media.$store, track = textTrack();
this._menu._hint.set(
track && isTrackCaptionKind(track) && track.mode === "showing" ? track.label : offLabel()
);
}
_watchControllerDisabled() {
this._menu._disable(this._isDisabled());
}
_isDisabled() {
const { textTracks } = this._media.$store;
return textTracks().filter(isTrackCaptionKind).length === 0;
}
_onChange(event) {
if (this._isDisabled())
return;
const radioGroup = event.target;
const value = radioGroup.value;
if (value === "off") {
const track = this._media.textTracks.selected;
if (track) {
const index2 = this._media.textTracks.toArray().indexOf(track);
this._media.remote.changeTextTrackMode(index2, "disabled", event);
}
return;
}
const index = this._media.textTracks.toArray().findIndex((track) => track.label.toLowerCase() === value);
if (index >= 0)
this._media.remote.changeTextTrackMode(index, "showing", event);
}
_getValue() {
const { textTrack, textTracks } = this._media.$store, track = textTrack();
return track && isTrackCaptionKind(track) && track.mode === "showing" ? track.label.toLowerCase() : "off";
}
_getOptions() {
const { offLabel } = this.$props, { textTracks } = this._media.$store;
return [
{ value: "off", content: () => (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$6), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, offLabel);
return $$_root;
})() },
...textTracks().filter(isTrackCaptionKind).map((track) => ({
value: track.label.toLowerCase(),
content: () => (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_2$1), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, track.label);
return $$_root;
})()
}))
];
}
render() {
const { radioGroupClass } = this.$props;
return renderRadioGroup({
value: this._getValue.bind(this),
options: this._getOptions.bind(this),
radioGroupClass,
onChange: this._onChange.bind(this)
});
}
}
class CaptionsMenuButton extends MenuButton {
static el = defineElement({
tagName: "media-captions-menu-button",
props: { disabled: false, label: "Captions" }
});
render() {
const { label } = this.$props;
return renderMenuButtonContent({
label,
iconPaths: Icon$25
});
}
}
const $$_templ$5 = /* @__PURE__ */ $$_create_template(`<!$><span part="label"><!$></span>`);
class PlaybackRateMenuItems extends MenuItems {
static el = defineElement({
tagName: "media-playback-rate-menu-items",
props: {
normalLabel: "Normal",
rates: [0.25, 0.5, 0.75, 1, 1.25, 1.5, 1.75, 2],
radioGroupClass: null,
radioClass: null,
radioCheckClass: null
}
});
static register = [RadioGroup, Radio];
_media;
constructor(instance) {
super(instance);
this._media = useMedia();
}
onConnect(el) {
effect(this._watchHintText.bind(this));
const { radioClass, radioCheckClass } = this.$props;
new ClassManager(el)._observe("media-radio", radioClass)._observe('[part="check"]', radioCheckClass);
}
_watchHintText() {
const { normalLabel } = this.$props, { playbackRate } = this._media.$store, rate = playbackRate();
this._menu._hint.set(rate === 1 ? normalLabel() : rate + "\xD7");
}
_onChange(event) {
const radioGroup = event.target;
this._media.remote.changePlaybackRate(+radioGroup.value, event);
}
_getValue() {
const { playbackRate } = this._media.$store;
return playbackRate() + "";
}
_getOptions() {
const { rates, normalLabel } = this.$props;
return rates().map((rate) => ({
value: rate + "",
content: () => (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$5), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, () => rate === 1 ? normalLabel() : rate + "\xD7");
return $$_root;
})()
}));
}
render() {
const { radioGroupClass } = this.$props;
return renderRadioGroup({
value: this._getValue.bind(this),
options: this._getOptions.bind(this),
radioGroupClass,
onChange: this._onChange.bind(this)
});
}
}
var Icon$55 = `<path d="M25.14 25.1089C25.0171 25.2532 24.8356 25.3333 24.646 25.3333H22.8124C22.1084 25.3333 21.7734 24.1872 22.2745 23.6927C23.9161 22.0729 24.9336 19.822 24.9336 17.3333C24.9336 12.3997 20.9336 8.39973 16 8.39973C11.0664 8.39973 7.06641 12.3997 7.06641 17.3333C7.06641 19.822 8.08389 22.0729 9.72555 23.6927C10.2266 24.1872 9.89155 25.3333 9.18762 25.3333H7.35398C7.16436 25.3333 6.98294 25.2532 6.86001 25.1089C5.07703 23.015 4 20.2991 4 17.3333C4 10.7057 9.3724 5.33333 16 5.33333C22.6276 5.33333 28 10.7057 28 17.3333C28 20.2991 26.923 23.015 25.14 25.1089Z" fill="currentColor"/> <path d="M21.1992 14.3399C21.4595 14.0796 21.4595 13.6575 21.1992 13.3971L20.2564 12.4543C19.996 12.194 19.5739 12.194 19.3136 12.4543L16.4492 15.3187C16.4185 15.3493 16.3749 15.3629 16.332 15.3568C16.2236 15.3414 16.1127 15.3334 16 15.3334C14.7113 15.3334 13.6667 16.378 13.6667 17.6667C13.6667 18.9554 14.7113 20 16 20C17.2887 20 18.3333 18.9554 18.3333 17.6667C18.3333 17.5464 18.3242 17.4283 18.3067 17.313C18.3001 17.2696 18.3136 17.2255 18.3446 17.1945L21.1992 14.3399Z" fill="currentColor"/>`;
class PlaybackRateMenuButton extends MenuButton {
static el = defineElement({
tagName: "media-playback-rate-menu-button",
props: { disabled: false, label: "Speed" }
});
render() {
const { label } = this.$props;
return renderMenuButtonContent({
label,
iconPaths: Icon$55
});
}
}
const $$_templ$4 = /* @__PURE__ */ $$_create_template(`<!$><span><!$></span>`), $$_templ_2 = /* @__PURE__ */ $$_create_template(`<!$><span part="label"><!$></span>`), $$_templ_3 = /* @__PURE__ */ $$_create_template(`<!$><span part="info"><!$></span>`);
class QualityMenuItems extends MenuItems {
static el = defineElement({
tagName: "media-quality-menu-items",
props: {
autoLabel: "Auto",
hideBitrate: false,
radioGroupClass: null,
radioClass: null,
radioCheckClass: null
}
});
static register = [RadioGroup, Radio];
_media;
_sortedQualities = computed(() => {
const { qualities } = this._media.$store;
return [...qualities()].sort(
(a, b) => b.height === a.height ? b.bitrate - a.bitrate : b.height - a.height
);
});
constructor(instance) {
super(instance);
this._media = useMedia();
}
onConnect(el) {
effect(this._watchControllerDisabled.bind(this));
effect(this._watchHintText.bind(this));
const { radioClass, radioCheckClass } = this.$props;
new ClassManager(el)._observe("media-radio", radioClass)._observe('[part="check"]', radioCheckClass);
}
_watchHintText() {
const { autoLabel } = this.$props, { autoQuality, quality } = this._media.$store, qualityText = quality() ? quality().height + "p" : "";
this._menu._hint.set(!autoQuality() ? qualityText : autoLabel() + ` (${qualityText})`);
}
_watchControllerDisabled() {
const { qualities } = this._media.$store;
this._menu._disable(qualities().length === 0);
}
_isDisabled() {
const { canSetQuality, qualities } = this._media.$store;
return !canSetQuality() || qualities().length === 0;
}
_onChange(event) {
if (this._isDisabled())
return;
const radioGroup = event.target, value = radioGroup.value;
if (value === "auto") {
this._media.remote.changeQuality(-1, event);
return;
}
const { qualities } = this._media.$store, index = peek(qualities).findIndex((quality) => this._getQualityId(quality) === value);
if (index >= 0)
this._media.remote.changeQuality(index, event);
}
_getValue() {
const { quality, autoQuality } = this._media.$store;
if (autoQuality())
return "auto";
const currentQuality = quality();
return currentQuality ? this._getQualityId(currentQuality) : "auto";
}
_getQualityId(quality) {
return quality.height + "_" + quality.bitrate;
}
_getOptions() {
const { autoLabel, hideBitrate } = this.$props;
return [
{ value: "auto", content: () => (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$4), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, autoLabel);
return $$_root;
})() },
...this._sortedQualities().map((quality) => {
const rate = `${round(quality.bitrate / 1e6, 2)} Mbps`;
return {
value: this._getQualityId(quality),
content: () => [
(() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_2), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, quality.height + "p");
return $$_root;
})(),
(() => {
const $$_signal = $$_computed(
() => !hideBitrate() && (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ_3), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, rate);
return $$_root;
})()
);
$$_signal();
return $$_signal;
})()
]
};
})
];
}
render() {
const { radioGroupClass } = this.$props;
return renderRadioGroup({
value: this._getValue.bind(this),
options: this._getOptions.bind(this),
radioGroupClass,
onChange: this._onChange.bind(this)
});
}
}
var Icon$83 = `<path d="M18.6669 10.4001C18.6669 10.7683 18.3684 11.0667 18.0002 11.0667H16.2668C15.8987 11.0667 15.6002 10.7683 15.6002 10.4001V9.86674C15.6002 9.7931 15.5405 9.73341 15.4669 9.73341H5.99998C5.63179 9.73341 5.33331 9.43493 5.33331 9.06674V7.33341C5.33331 6.96522 5.63179 6.66674 5.99998 6.66674H15.4669C15.5405 6.66674 15.6002 6.60704 15.6002 6.53341V6.00007C15.6002 5.63188 15.8987 5.3334 16.2668 5.3334H18.0002C18.3684 5.3334 18.6669 5.63188 18.6669 6.00007V10.4001Z" fill="currentColor"/> <path d="M11.3334 18.8668C11.7016 18.8668 12.0001 18.5683 12.0001 18.2001V13.8001C12.0001 13.4319 11.7016 13.1335 11.3334 13.1335H9.60006C9.23187 13.1335 8.93339 13.4319 8.93339 13.8001V14.3335C8.93339 14.4071 8.8737 14.4668 8.80006 14.4668H6.00006C5.63187 14.4668 5.33339 14.7653 5.33339 15.1335V16.8668C5.33339 17.235 5.63187 17.5335 6.00006 17.5335H8.80006C8.8737 17.5335 8.93339 17.5932 8.93339 17.6668V18.2001C8.93339 18.5683 9.23187 18.8668 9.60006 18.8668H11.3334Z" fill="currentColor"/> <path d="M18.6667 26.0001C18.6667 26.3683 18.3682 26.6668 18 26.6668H16.2667C15.8985 26.6668 15.6 26.3683 15.6 26.0001V25.4668C15.6 25.3931 15.5403 25.3334 15.4667 25.3334H6.00014C5.63195 25.3334 5.33348 25.0349 5.33348 24.6668V22.9334C5.33348 22.5652 5.63195 22.2668 6.00014 22.2668H15.4667C15.5403 22.2668 15.6 22.2071 15.6 22.1334V21.6001C15.6 21.2319 15.8985 20.9334 16.2667 20.9334H18C18.3682 20.9334 18.6667 21.2319 18.6667 21.6001V26.0001Z" fill="currentColor"/> <path d="M22 24.6668C22 25.0349 22.2985 25.3334 22.6667 25.3334H26.0001C26.3683 25.3334 26.6668 25.0349 26.6668 24.6668V22.9334C26.6668 22.5652 26.3683 22.2668 26.0001 22.2668H22.6667C22.2985 22.2668 22 22.5652 22 22.9334V24.6668Z" fill="currentColor"/> <path d="M16.0001 17.5335C15.6319 17.5335 15.3334 17.235 15.3334 16.8668V15.1335C15.3334 14.7653 15.6319 14.4668 16.0001 14.4668H26.0001C26.3683 14.4668 26.6667 14.7653 26.6667 15.1335V16.8668C26.6667 17.235 26.3683 17.5335 26.0001 17.5335H16.0001Z" fill="currentColor"/> <path d="M22.0002 9.06674C22.0002 9.43493 22.2987 9.73341 22.6669 9.73341H26C26.3682 9.73341 26.6666 9.43493 26.6666 9.06674V7.3334C26.6666 6.96521 26.3682 6.66674 26 6.66674H22.6669C22.2987 6.66674 22.0002 6.96522 22.0002 7.33341V9.06674Z" fill="currentColor"/>`;
class QualityMenuButton extends MenuButton {
static el = defineElement({
tagName: "media-quality-menu-button",
props: { disabled: false, label: "Quality" }
});
render() {
const { label } = this.$props;
return renderMenuButtonContent({
label,
iconPaths: Icon$83
});
}
}
class Gesture extends Component {
static el = defineElement({
tagName: "media-gesture",
props: {
event: void 0,
action: void 0
}
});
_media;
_outlet = null;
onAttach() {
const { event, action } = this.$props;
this.setAttributes({
event,
action
});
}
onConnect() {
this._media = useMedia();
scopedRaf(() => {
this._outlet = this._media.player.querySelector("media-outlet");
effect(this._attachListener.bind(this));
});
}
_attachListener() {
let eventType = this.$props.event();
if (!this._outlet || !eventType)
return;
if (/^dbl/.test(eventType)) {
eventType = eventType.split(/^dbl/)[1];
}
listenEvent(this._outlet, eventType, this._acceptEvent.bind(this));
}
_presses = 0;
_pressTimerId = -1;
_acceptEvent(event) {
if (!this._inBounds(event) || isPointerEvent(event) && (event.button !== 0 || this._media.activeMenu)) {
return;
}
event.MEDIA_GESTURE = true;
event.preventDefault();
const isDblEvent = peek(this.$props.event)?.startsWith("dbl");
if (!isDblEvent) {
if (this._presses === 0) {
setTimeout(() => {
if (this._presses === 1)
this._handleEvent(event);
}, 250);
}
} else if (this._presses === 1) {
queueMicrotask(() => this._handleEvent(event));
clearTimeout(this._pressTimerId);
this._presses = 0;
return;
}
if (this._presses === 0) {
this._pressTimerId = window.setTimeout(() => {
this._presses = 0;
}, 275);
}
this._presses++;
}
_handleEvent(event) {
this.el.setAttribute("data-triggered", "");
requestAnimationFrame(() => {
if (this._isTopLayer()) {
this._performAction(peek(this.$props.action), event);
}
requestAnimationFrame(() => {
this.el.removeAttribute("data-triggered");
});
});
}
/** Validate event occurred in gesture bounds. */
_inBounds(event) {
if (!this.el)
return false;
if (isPointerEvent(event) || isMouseEvent(event) || isTouchEvent(event)) {
const touch = isTouchEvent(event) ? event.touches[0] : void 0;
const clientX = touch?.clientX ?? event.clientX;
const clientY = touch?.clientY ?? event.clientY;
const rect = this.el.getBoundingClientRect();
const inBounds = clientY >= rect.top && clientY <= rect.bottom && clientX >= rect.left && clientX <= rect.right;
return event.type.includes("leave") ? !inBounds : inBounds;
}
return true;
}
/** Validate gesture has the highest z-index in this triggered group. */
_isTopLayer() {
const gestures = this._media.player.querySelectorAll(
"media-gesture[data-triggered]"
);
return Array.from(gestures).sort(
(a, b) => +getComputedStyle(b).zIndex - +getComputedStyle(a).zIndex
)[0]?.component === this;
}
_performAction(action, trigger) {
if (!action)
return;
const [method, value] = action.replace(/:([a-z])/, "-$1").split(":");
if (action.includes(":fullscreen")) {
this._media.remote.toggleFullscreen("prefer-media", trigger);
} else if (action.includes("seek:")) {
this._media.remote.seek(peek(this._media.$store.currentTime) + (+value || 0), trigger);
} else {
this._media.remote[kebabToCamelCase(method)](trigger);
}
}
}
const $$_templ$3 = /* @__PURE__ */ $$_create_template(`<!$><svg part="icon" fill="none" viewBox="0 0 120 120" aria-hidden="true"><circle part="track" cx="60" cy="60" r="54" stroke="currentColor"></circle><circle part="track-fill" cx="60" cy="60" r="54" stroke="currentColor" pathLength="100"></circle></svg>`);
class BufferingIndicator extends Component {
static el = defineElement({
tagName: "media-buffering-indicator"
});
_media;
onAttach() {
this._media = useMedia();
this.setAttributes({
"data-buffering": computed(this._isBuffering.bind(this))
});
}
_isBuffering() {
const { canPlay, waiting } = this._media.$store;
return !canPlay() || waiting();
}
render() {
return $$_next_template($$_templ$3);
}
}
class CaptionsTextRenderer {
constructor(_renderer) {
this._renderer = _renderer;
}
priority = 10;
_track = null;
_disposal = createDisposalBin();
attach() {
}
canRender() {
return true;
}
detach() {
this._disposal.empty();
this._renderer.reset();
this._track = null;
}
changeTrack(track) {
if (!track || this._track === track)
return;
this._disposal.empty();
if (track.readyState < 2) {
this._renderer.reset();
this._disposal.add(
listenEvent(track, "load", () => this._changeTrack(track), { once: true })
);
} else {
this._changeTrack(track);
}
this._disposal.add(
listenEvent(track, "add-cue", (event) => {
this._renderer.addCue(event.detail);
}),
listenEvent(track, "remove-cue", (event) => {
this._renderer.removeCue(event.detail);
})
);
this._track = track;
}
_changeTrack(track) {
this._renderer.changeTrack({
cues: [...track.cues],
regions: [...track.regions]
});
}
}
class Captions extends Component {
static el = defineElement({
tagName: "media-captions",
props: { textDir: "ltr" }
});
_media;
_renderer;
_textRenderer;
onAttach() {
this._media = useMedia();
this.setAttributes({
"aria-hidden": $ariaBool(this._isHidden.bind(this))
});
}
onConnect(el) {
this._renderer = new CaptionsRenderer(el);
this._textRenderer = new CaptionsTextRenderer(this._renderer);
effect(this._watchViewType.bind(this));
}
onDisconnect() {
if (this._textRenderer) {
this._textRenderer.detach();
this._media.textRenderers.remove(this._textRenderer);
}
this._renderer?.destroy();
}
_isHidden() {
const { textTrack } = this._media.$store, track = textTrack();
return !track || !isTrackCaptionKind(track);
}
_watchViewType() {
const { viewType } = this._media.$store;
if (viewType() === "audio") {
return this._setupAudioView();
} else {
return this._setupVideoView();
}
}
_setupAudioView() {
effect(this._onTrackChange.bind(this));
return () => {
this.el.textContent = "";
};
}
_onTrackChange() {
if (this._isHidden())
return;
const { textTrack } = this._media.$store;
this._onCueChange();
listenEvent(textTrack(), "cue-change", this._onCueChange.bind(this));
effect(this._onUpdateTimedNodes.bind(this));
}
_onCueChange() {
this.el.textContent = "";
const { currentTime, textTrack } = this._media.$store, time = peek(currentTime), activeCues = peek(textTrack).activeCues;
for (const cue of activeCues) {
const cueEl = document.createElement("div");
cueEl.setAttribute("part", "cue");
cueEl.innerHTML = renderVTTCueString(cue, time);
this.el.append(cueEl);
}
}
_onUpdateTimedNodes() {
const { currentTime } = this._media.$store;
updateTimedVTTCueNodes(this.el, currentTime());
}
_setupVideoView() {
effect(this._watchTextDirection.bind(this));
effect(this._watchMediaTime.bind(this));
this._media.textRenderers.add(this._textRenderer);
return () => {
this.el.textContent = "";
this._textRenderer.detach();
this._media.textRenderers.remove(this._textRenderer);
};
}
_watchTextDirection() {
this._renderer.dir = this.$props.textDir();
}
_watchMediaTime() {
if (this._isHidden())
return;
const { currentTime } = this._media.$store;
this._renderer.currentTime = currentTime();
}
}
const $$_templ$2 = /* @__PURE__ */ $$_create_template(`<!$><div part="container"><div part="text">LIVE</div></div>`);
class LiveIndicator extends Component {
static el = defineElement({
tagName: "media-live-indicator"
});
_media;
constructor(instance) {
super(instance);
this._media = useMedia();
new FocusVisibleController(instance);
}
onAttach(el) {
const { live, liveEdge } = this._media.$store;
setARIALabel(el, this._getLabel.bind(this));
this.setAttributes({
tabindex: this._getTabIndex.bind(this),
role: this._getRole.bind(this),
"data-live": live,
"data-live-edge": liveEdge,
"data-media-button": true
});
}
onConnect(el) {
onPress(el, this._onPress.bind(this));
}
_getLabel() {
const { live } = this._media.$store;
return live() ? "Go live" : null;
}
_getTabIndex() {
const { live } = this._media.$store;
return live() ? 0 : null;
}
_getRole() {
const { live } = this._media.$store;
return live() ? "button" : null;
}
_onPress(event) {
const { liveEdge } = this._media.$store;
if (liveEdge())
return;
this._media.remote.seekToLiveEdge(event);
}
render() {
return $$_next_template($$_templ$2);
}
}
const $$_templ$1 = /* @__PURE__ */ $$_create_template(`<!$><img part="img" />`);
class Poster extends Component {
static el = defineElement({
tagName: "media-poster",
props: { alt: void 0 }
});
_media;
_imgLoading = signal(true);
_imgError = signal(false);
_imgSrc;
_imgAlt;
onAttach(el) {
this._media = useMedia();
this._imgSrc = computed(this._getImgSrc.bind(this));
this._imgAlt = this._getImgAlt.bind(this);
this.setAttributes({
"data-loading": this._imgLoading,
"aria-hidden": $ariaBool(this._isHidden.bind(this))
});
}
onConnect(el) {
const { canLoad, poster } = this._media.$store;
window.requestAnimationFrame(() => {
if (!canLoad())
preconnect(poster());
});
effect(this._onLoadStart.bind(this));
}
_isHidden() {
const { poster } = this._media.$store;
return this._imgError() || !poster();
}
_getImgSrc() {
const { canLoad, poster } = this._media.$store;
return canLoad() && poster().length ? poster() : null;
}
_getImgAlt() {
return this._imgSrc() ? this.$props.alt() : null;
}
_onLoadStart() {
const { canLoad, poster } = this._media.$store;
const isLoading = canLoad() && !!poster();
this._imgLoading.set(isLoading);
this._imgError.set(false);
}
_onLoad() {
this._imgLoading.set(false);
}
_onError() {
this._imgLoading.set(false);
this._imgError.set(true);
}
render() {
const { crossorigin } = this._media.$store;
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ$1);
$$_effect(() => $$_attr($$_root, "src", this._imgSrc()));
$$_effect(() => $$_attr($$_root, "alt", this._imgAlt()));
$$_effect(() => $$_attr($$_root, "crossorigin", crossorigin()));
$$_listen($$_root, "load", this._onLoad.bind(this));
$$_listen($$_root, "error", this._onError.bind(this));
return $$_root;
})();
}
}
const $$_templ = /* @__PURE__ */ $$_create_template(`<!$><span><!$></span>`);
class Time extends Component {
static el = defineElement({
tagName: "media-time",
props: {
type: "current",
showHours: false,
padHours: null,
padMinutes: null,
remainder: false
}
});
_media;
_time;
onAttach() {
this._media = useMedia();
this._time = computed(this._getTime.bind(this));
}
_getTime() {
const { type, remainder, padHours, padMinutes, showHours } = this.$props, seconds = this._getSeconds(type()), duration = this._media.$store.duration();
if (!Number.isFinite(seconds + duration))
return "LIVE";
const time = remainder() ? Math.max(0, duration - seconds) : seconds;
return formatTime(
time,
padHours(),
isNull(padMinutes()) ? time >= 3600 : padMinutes(),
showHours()
);
}
_getSeconds(type) {
const { bufferedEnd, duration, currentTime } = this._media.$store;
switch (type) {
case "buffered":
return bufferedEnd();
case "duration":
return duration();
default:
return currentTime();
}
}
render() {
return (() => {
const [$$_root, $$_walker] = $$_create_walker($$_templ), $$_expr = $$_walker.nextNode();
$$_insert_at_marker_lite($$_expr, this._time);
return $$_root;
})();
}
}
function setupPreviewStyles(preview, orientation) {
let rect = preview.getBoundingClientRect(), styles = {
"--computed-width": rect.width + "px",
"--computed-height": rect.height + "px",
"--preview-width": "var(--media-slider-preview-width, var(--computed-width))",
"--preview-height": "var(--media-slider-preview-height, var(--computed-height))"
};
if (orientation !== "vertical") {
styles = {
...styles,
"--preview-width-half": "calc(var(--preview-width) / 2)",
"--preview-left-clamp": "max(var(--preview-width-half), var(--slider-pointer-percent))",
"--preview-right-clamp": "calc(100% - var(--preview-width-half))",
"--preview-left": "min(var(--preview-left-clamp), var(--preview-right-clamp))"
};
} else {
styles = {
...styles,
"--preview-height-half": "calc(var(--preview-height) / 2)",
"--preview-top-clamp": "max(var(--preview-height-half), var(--slider-pointer-percent))",
"--preview-bottom-clamp": "calc(100% - var(--preview-height-half))",
"--preview-bottom": "min(var(--preview-top-clamp), var(--preview-bottom-clamp))"
};
}
for (const name of Object.keys(styles)) {
preview.style.setProperty(name, styles[name]);
}
}
var preview = /*#__PURE__*/Object.freeze({
__proto__: null,
setupPreviewStyles: setupPreviewStyles
});
export { AudioMenuButton as A, BufferingIndicator as B, CaptionButton as C, toggleButtonProps as D, SliderStoreFactory as E, FullscreenButton as F, Gesture as G, LiveIndicator as L, MuteButton as M, Poster as P, QualityMenuButton as Q, RadioGroup as R, SeekButton as S, Tooltip as T, VolumeSlider as V, Thumbnail as a, PlayButton as b, PIPButton as c, Slider as d, TimeSlider as e, SliderThumbnail as f, SliderValue as g, SliderVideo as h, Time as i, ToggleButton as j, Captions as k, Menu as l, MenuButton as m, MenuItems as n, Radio as o, AudioMenuItems as p, CaptionsMenuButton as q, CaptionsMenuItems as r, ChaptersMenuItems as s, QualityMenuItems as t, PlaybackRateMenuButton as u, PlaybackRateMenuItems as v, round as w, FocusVisibleController as x, clampNumber as y, getNumberOfDecimalPlaces as z };