node_modules ignore

This commit is contained in:
2025-05-08 23:43:47 +02:00
parent e19d52f172
commit 4574544c9f
65041 changed files with 10593536 additions and 0 deletions

109
server/node_modules/custom-media-element/README.md generated vendored Normal file
View File

@@ -0,0 +1,109 @@
# Custom Media Element
[![NPM Version](https://img.shields.io/npm/v/custom-media-element?style=flat-square&color=informational)](https://www.npmjs.com/package/custom-media-element)
[![NPM Downloads](https://img.shields.io/npm/dm/custom-media-element?style=flat-square&color=informational&label=npm)](https://www.npmjs.com/package/custom-media-element)
[![jsDelivr hits (npm)](https://img.shields.io/jsdelivr/npm/hm/custom-media-element?style=flat-square&color=%23FF5627)](https://www.jsdelivr.com/package/npm/custom-media-element)
[![npm bundle size](https://img.shields.io/bundlephobia/minzip/custom-media-element?style=flat-square&color=success&label=gzip)](https://bundlephobia.com/result?p=custom-media-element)
[![Codecov](https://img.shields.io/codecov/c/github/muxinc/custom-media-element?style=flat-square)](https://app.codecov.io/gh/muxinc/custom-media-element)
A custom element for extending the native media elements (`<audio>` or `<video>`).
## Usage
```js
import { CustomVideoElement } from 'custom-media-element';
class MyCustomVideoElement extends CustomVideoElement {
constructor() {
super();
}
// Override the play method.
play() {
return super.play()
}
// Override the src getter & setter.
get src() {
return super.src;
}
set src(src) {
super.src = src;
}
}
if (globalThis.customElements && !globalThis.customElements.get('my-custom-video')) {
globalThis.customElements.define('my-custom-video', MyCustomVideoElement);
}
export default MyCustomVideoElement;
```
```html
<my-custom-video
src="https://stream.mux.com/A3VXy02VoUinw01pwyomEO3bHnG4P32xzV7u1j1FSzjNg/low.mp4"
></my-custom-video>
```
## Interfaces
```ts
export const Events: string[];
export const audioTemplate: HTMLTemplateElement;
export const videoTemplate: HTMLTemplateElement;
export class CustomAudioElement extends HTMLAudioElement implements HTMLAudioElement {
static readonly observedAttributes: string[];
static Events: string[];
static template: HTMLTemplateElement;
readonly nativeEl: HTMLAudioElement;
attributeChangedCallback(attrName: string, oldValue?: string | null, newValue?: string | null): void;
connectedCallback(): void;
disconnectedCallback(): void;
handleEvent(event: Event): void;
}
export class CustomVideoElement extends HTMLVideoElement implements HTMLVideoElement {
static readonly observedAttributes: string[];
static Events: string[];
static template: HTMLTemplateElement;
readonly nativeEl: HTMLVideoElement;
attributeChangedCallback(attrName: string, oldValue?: string | null, newValue?: string | null): void;
connectedCallback(): void;
disconnectedCallback(): void;
handleEvent(event: Event): void;
}
type CustomMediaElementConstructor<T> = {
readonly observedAttributes: string[];
Events: string[];
template: HTMLTemplateElement;
new(): T
};
export function CustomMediaMixin(superclass: any, options: { tag: 'video', is?: string }):
CustomMediaElementConstructor<CustomVideoElement>;
export function CustomMediaMixin(superclass: any, options: { tag: 'audio', is?: string }):
CustomMediaElementConstructor<CustomAudioElement>;
```
## Related
- [Media Chrome](https://github.com/muxinc/media-chrome) Your media player's dancing suit. 🕺
- [`<hls-video>`](https://github.com/muxinc/media-elements/tree/main/packages/hls-video-element) A custom element for playing HTTP Live Streaming (HLS) videos.
- [`<youtube-video>`](https://github.com/muxinc/media-elements/tree/main/packages/youtube-video-element) A custom element for the YouTube player.
- [`<vimeo-video>`](https://github.com/muxinc/media-elements/tree/main/packages/vimeo-video-element) A custom element for the Vimeo player.
- [`<spotify-audio>`](https://github.com/muxinc/media-elements/tree/main/packages/spotify-audio-element) A custom element for the Spotify player.
- [`<jwplayer-video>`](https://github.com/muxinc/media-elements/tree/main/packages/jwplayer-video-element) A custom element for the JW player.
- [`<wistia-video>`](https://github.com/muxinc/media-elements/tree/main/packages/wistia-video-element) A custom element for the Wistia player.
- [`<cloudflare-video>`](https://github.com/muxinc/media-elements/tree/main/packages/cloudflare-video-element) A custom element for the Cloudflare player.
- [`<videojs-video>`](https://github.com/muxinc/media-elements/tree/main/packages/videojs-video-element) A custom element for Video.js.
- [`<castable-video>`](https://github.com/muxinc/media-elements/tree/main/packages/castable-video) Cast your video element to the big screen with ease!
- [`<mux-player>`](https://github.com/muxinc/elements/tree/main/packages/mux-player) The official Mux-flavored video player custom element.
- [`<mux-video>`](https://github.com/muxinc/elements/tree/main/packages/mux-video) A Mux-flavored HTML5 video element w/ hls.js and Mux data builtin.

View File

@@ -0,0 +1,45 @@
export const Events: string[];
export const audioTemplate: HTMLTemplateElement;
export const videoTemplate: HTMLTemplateElement;
export class CustomAudioElement extends HTMLAudioElement implements HTMLAudioElement {
static readonly observedAttributes: string[];
static Events: string[];
static template: HTMLTemplateElement;
readonly nativeEl: HTMLAudioElement;
attributeChangedCallback(attrName: string, oldValue?: string | null, newValue?: string | null): void;
connectedCallback(): void;
disconnectedCallback(): void;
init(): void;
handleEvent(event: Event): void;
}
export class CustomVideoElement extends HTMLVideoElement implements HTMLVideoElement {
static readonly observedAttributes: string[];
static Events: string[];
static template: HTMLTemplateElement;
readonly nativeEl: HTMLVideoElement;
attributeChangedCallback(attrName: string, oldValue?: string | null, newValue?: string | null): void;
connectedCallback(): void;
disconnectedCallback(): void;
init(): void;
handleEvent(event: Event): void;
}
export type CustomMediaElementConstructor<T> = {
readonly observedAttributes: string[];
Events: string[];
template: HTMLTemplateElement;
new(): T;
};
export type CustomVideoElementConstructor = CustomMediaElementConstructor<CustomVideoElement>;
export type CustomAudioElementConstructor = CustomMediaElementConstructor<CustomAudioElement>;
export function CustomMediaMixin<T = HTMLElement>(superclass: T, options: { tag: 'video', is?: string }):
T & CustomVideoElementConstructor;
export function CustomMediaMixin<T = HTMLElement>(superclass: T, options: { tag: 'audio', is?: string }):
T & CustomAudioElementConstructor;

View File

@@ -0,0 +1,462 @@
/**
* Custom Media Element
* Based on https://github.com/muxinc/custom-video-element - Mux - MIT License
*
* The goal is to create an element that works just like the video element
* but can be extended/sub-classed, because native elements cannot be
* extended today across browsers.
*/
// The onevent like props are weirdly set on the HTMLElement prototype with other
// generic events making it impossible to pick these specific to HTMLMediaElement.
export const Events = [
'abort',
'canplay',
'canplaythrough',
'durationchange',
'emptied',
'encrypted',
'ended',
'error',
'loadeddata',
'loadedmetadata',
'loadstart',
'pause',
'play',
'playing',
'progress',
'ratechange',
'seeked',
'seeking',
'stalled',
'suspend',
'timeupdate',
'volumechange',
'waiting',
'waitingforkey',
'resize',
'enterpictureinpicture',
'leavepictureinpicture',
'webkitbeginfullscreen',
'webkitendfullscreen',
'webkitpresentationmodechanged',
];
function getAudioTemplateHTML(attrs) {
return /*html*/ `
<style>
:host {
display: inline-flex;
line-height: 0;
flex-direction: column;
justify-content: end;
}
audio {
width: 100%;
}
</style>
<slot name="media">
<audio${serializeAttributes(attrs)}></audio>
</slot>
<slot></slot>
`;
}
// If the `media` slot is used leave the styling up to the user.
// It's a more consistent behavior pre and post custom element upgrade.
function getVideoTemplateHTML(attrs) {
return /*html*/ `
<style>
:host {
display: inline-block;
line-height: 0;
}
video {
max-width: 100%;
max-height: 100%;
min-width: 100%;
min-height: 100%;
object-fit: var(--media-object-fit, contain);
object-position: var(--media-object-position, 50% 50%);
}
video::-webkit-media-text-track-container {
transform: var(--media-webkit-text-track-transform);
transition: var(--media-webkit-text-track-transition);
}
</style>
<slot name="media">
<video${serializeAttributes(attrs)}></video>
</slot>
<slot></slot>
`;
}
/**
* @see https://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/
*/
export const CustomMediaMixin = (superclass, { tag, is }) => {
// `is` makes it possible to extend a custom built-in. e.g. castable-video
const nativeElTest = globalThis.document?.createElement?.(tag, { is });
const nativeElProps = nativeElTest ? getNativeElProps(nativeElTest) : [];
return class CustomMedia extends superclass {
static getTemplateHTML = tag.endsWith('audio') ? getAudioTemplateHTML : getVideoTemplateHTML;
static shadowRootOptions = { mode: 'open' };
static Events = Events;
static #isDefined;
static get observedAttributes() {
CustomMedia.#define();
// Include any attributes from the custom built-in.
const natAttrs = nativeElTest?.constructor?.observedAttributes ?? [];
return [
...natAttrs,
'autopictureinpicture',
'disablepictureinpicture',
'disableremoteplayback',
'autoplay',
'controls',
'controlslist',
'crossorigin',
'loop',
'muted',
'playsinline',
'poster',
'preload',
'src',
];
}
static #define() {
if (this.#isDefined) return;
this.#isDefined = true;
const propsToAttrs = new Set(this.observedAttributes);
// defaultMuted maps to the muted attribute, handled manually below.
propsToAttrs.delete('muted');
// Passthrough native el functions from the custom el to the native el
for (let prop of nativeElProps) {
if (prop in this.prototype) continue;
const type = typeof nativeElTest[prop];
if (type == 'function') {
// Function
this.prototype[prop] = function (...args) {
this.#init();
const fn = () => {
if (this.call) return this.call(prop, ...args);
return this.nativeEl[prop].apply(this.nativeEl, args);
};
return fn();
};
} else {
// Some properties like src, preload, defaultMuted are handled manually.
// Getter
let config = {
get() {
this.#init();
let attr = prop.toLowerCase();
if (propsToAttrs.has(attr)) {
const val = this.getAttribute(attr);
return val === null ? false : val === '' ? true : val;
}
return this.get?.(prop) ?? this.nativeEl?.[prop];
},
};
if (prop !== prop.toUpperCase()) {
// Setter (not a CONSTANT)
config.set = function (val) {
this.#init();
let attr = prop.toLowerCase();
if (propsToAttrs.has(attr)) {
if (val === true || val === false || val == null) {
this.toggleAttribute(attr, Boolean(val));
} else {
this.setAttribute(attr, val);
}
return;
}
if (this.set) {
this.set(prop, val);
return;
}
this.nativeEl[prop] = val;
};
}
Object.defineProperty(this.prototype, prop, config);
}
}
}
#isInit;
#nativeEl;
#childMap = new Map();
#childObserver;
constructor() {
super();
// If the custom element is defined before the custom element's HTML is parsed
// no attributes will be available in the constructor (construction process).
// Wait until initializing in the attributeChangedCallback or
// connectedCallback or accessing any properties.
}
get nativeEl() {
this.#init();
return (
this.#nativeEl ??
this.querySelector(':scope > [slot=media]') ??
this.querySelector(tag) ??
this.shadowRoot.querySelector(tag)
);
}
set nativeEl(val) {
this.#nativeEl = val;
}
get defaultMuted() {
return this.hasAttribute('muted');
}
set defaultMuted(val) {
this.toggleAttribute('muted', Boolean(val));
}
get src() {
return this.getAttribute('src');
}
set src(val) {
this.setAttribute('src', `${val}`);
}
get preload() {
return this.getAttribute('preload') ?? this.nativeEl?.preload;
}
set preload(val) {
this.setAttribute('preload', `${val}`);
}
#init() {
if (this.#isInit) return;
this.#isInit = true;
this.init();
}
init() {
if (!this.shadowRoot) {
this.attachShadow({ mode: 'open' });
const attrs = namedNodeMapToObject(this.attributes);
if (is) attrs.is = is;
if (tag) attrs.part = tag;
this.shadowRoot.innerHTML = this.constructor.getTemplateHTML(attrs);
}
// Neither Chrome or Firefox support setting the muted attribute
// after using document.createElement.
// Get around this by setting the muted property manually.
this.nativeEl.muted = this.hasAttribute('muted');
for (let prop of nativeElProps) {
this.#upgradeProperty(prop);
}
this.#childObserver = new MutationObserver(this.#syncMediaChildAttribute);
this.shadowRoot.addEventListener('slotchange', this);
this.#syncMediaChildren();
for (let type of this.constructor.Events) {
this.shadowRoot.addEventListener?.(type, this, true);
}
}
handleEvent(event) {
if (event.type === 'slotchange') {
this.#syncMediaChildren();
return;
}
if (event.target === this.nativeEl) {
// The video events are dispatched on the CustomMediaElement instance.
// This makes it possible to add event listeners before the element is upgraded.
this.dispatchEvent(new CustomEvent(event.type, { detail: event.detail }));
}
}
/**
* Keep some native child elements like track and source in sync.
* An unnamed <slot> will be filled with all of the custom element's
* top-level child nodes that do not have the slot attribute.
*/
#syncMediaChildren() {
const removeNativeChildren = new Map(this.#childMap);
this.shadowRoot
.querySelector('slot:not([name])')
.assignedElements({ flatten: true })
.filter((el) => ['track', 'source'].includes(el.localName))
.forEach((el) => {
// If the source or track is still in the assigned elements keep it.
removeNativeChildren.delete(el);
// Re-use clones if possible.
let clone = this.#childMap.get(el);
if (!clone) {
clone = el.cloneNode();
this.#childMap.set(el, clone);
this.#childObserver.observe(el, { attributes: true });
}
this.nativeEl.append?.(clone);
this.#enableDefaultTrack(clone);
});
removeNativeChildren.forEach((clone, el) => {
clone.remove();
this.#childMap.delete(el);
});
}
#syncMediaChildAttribute = (mutations) => {
for (let mutation of mutations) {
if (mutation.type === 'attributes') {
const { target, attributeName } = mutation;
const clone = this.#childMap.get(target);
clone?.setAttribute(attributeName, target.getAttribute(attributeName));
this.#enableDefaultTrack(clone);
}
}
};
#enableDefaultTrack(trackEl) {
// https://html.spec.whatwg.org/multipage/media.html#sourcing-out-of-band-text-tracks
// If there are any text tracks in the media element's list of text
// tracks whose text track kind is chapters or metadata that
// correspond to track elements with a default attribute set whose
// text track mode is set to disabled, then set the text track
// mode of all such tracks to hidden.
if (
trackEl.localName === 'track' &&
trackEl.default &&
(trackEl.kind === 'chapters' || trackEl.kind === 'metadata') &&
trackEl.track.mode === 'disabled'
) {
trackEl.track.mode = 'hidden';
}
}
#upgradeProperty(prop) {
// Sets properties that are set before the custom element is upgraded.
// https://web.dev/custom-elements-best-practices/#make-properties-lazy
if (Object.prototype.hasOwnProperty.call(this, prop)) {
const value = this[prop];
// Delete the set property from this instance.
delete this[prop];
// Set the value again via the (prototype) setter on this class.
this[prop] = value;
}
}
attributeChangedCallback(attrName, oldValue, newValue) {
// Initialize right after construction when the attributes become available.
this.#init();
this.#forwardAttribute(attrName, oldValue, newValue);
}
#forwardAttribute(attrName, oldValue, newValue) {
// Ignore a few that don't need to be passed.
if (['id', 'class'].includes(attrName)) {
return;
}
// Ignore setting custom attributes from the child class.
// They should not have any effect on the native element, it adds noise in the DOM.
if (
!CustomMedia.observedAttributes.includes(attrName) &&
this.constructor.observedAttributes.includes(attrName)
) {
return;
}
if (newValue === null) {
this.nativeEl.removeAttribute?.(attrName);
} else {
if (this.nativeEl.getAttribute?.(attrName) != newValue) {
this.nativeEl.setAttribute?.(attrName, newValue);
}
}
}
connectedCallback() {
this.#init();
}
};
};
function getNativeElProps(nativeElTest) {
// Map all native element properties to the custom element
// so that they're applied to the native element.
// Skipping HTMLElement because of things like "attachShadow"
// causing issues. Most of those props still need to apply to
// the custom element.
let nativeElProps = [];
// Walk the prototype chain up to HTMLElement.
// This will grab all super class props in between.
// i.e. VideoElement and MediaElement
for (
let proto = Object.getPrototypeOf(nativeElTest);
proto && proto !== HTMLElement.prototype;
proto = Object.getPrototypeOf(proto)
) {
nativeElProps.push(...Object.getOwnPropertyNames(proto));
}
return nativeElProps;
}
function serializeAttributes(attrs) {
let html = '';
for (const key in attrs) {
const value = attrs[key];
if (value === '') html += ` ${key}`;
else html += ` ${key}="${value}"`;
}
return html;
}
function namedNodeMapToObject(namedNodeMap) {
let obj = {};
for (let attr of namedNodeMap) {
obj[attr.name] = attr.value;
}
return obj;
}
export const CustomVideoElement = CustomMediaMixin(globalThis.HTMLElement ?? class {}, {
tag: 'video',
});
export const CustomAudioElement = CustomMediaMixin(globalThis.HTMLElement ?? class {}, {
tag: 'audio',
});

41
server/node_modules/custom-media-element/package.json generated vendored Normal file
View File

@@ -0,0 +1,41 @@
{
"name": "custom-media-element",
"version": "1.3.3",
"description": "A custom element for extending the native media elements (<audio> or <video>)",
"author": "@muxinc",
"license": "MIT",
"homepage": "https://github.com/muxinc/media-elements#readme",
"bugs": {
"url": "https://github.com/muxinc/media-elements/issues"
},
"repository": {
"type": "git",
"url": "git+https://github.com/muxinc/media-elements.git",
"directory": "packages/custom-media-element"
},
"files": [
"custom-media-element.d.ts"
],
"type": "module",
"main": "custom-media-element.js",
"types": "custom-media-element.d.ts",
"scripts": {
"lint": "eslint *.js",
"pretest": "esbuild custom-media-element.js --target=es2019 --bundle --outdir=dist --global-name=CustomMediaElement",
"test": "wet test test/eager-upgrade.html test/lazy-upgrade.html --coverage",
"serve": "wet serve --redirect :examples/ --cors"
},
"devDependencies": {
"esbuild": "^0.21.4",
"wet-run": "^1.2.2"
},
"keywords": [
"custom",
"element",
"video",
"audio",
"media",
"web",
"component"
]
}