New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

super-media-element

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

super-media-element - npm Package Compare versions

Comparing version 0.4.2 to 1.0.0

12

package.json
{
"name": "super-media-element",
"version": "0.4.2",
"version": "1.0.0",
"description": "Helps you create a custom element w/ a HTMLMediaElement API.",

@@ -19,12 +19,10 @@ "type": "module",

"scripts": {
"lint": "eslint *.js",
"lint": "npx eslint *.js",
"pretest": "esbuild super-media-element.js --format=iife --outdir=dist",
"test": "web-test-runner --config test/web-test-runner.config.js",
"dev": "npx serve ."
"test": "wet run test/lazy.html test/eager.html",
"dev": "wet serve --cors --redirect :example/"
},
"devDependencies": {
"@open-wc/testing": "^3.1.7",
"@web/test-runner": "^0.15.0",
"esbuild": "^0.17.3",
"eslint": "^8.32.0"
"wet-run": "^0.0.8"
},

@@ -31,0 +29,0 @@ "eslintConfig": {

# Super Media Element
[![Version](https://img.shields.io/npm/v/super-media-element?style=flat-square)](https://www.npmjs.com/package/super-media-element)
[![Badge size](https://img.badgesize.io/https://cdn.jsdelivr.net/npm/super-media-element/+esm?compression=gzip&label=gzip&style=flat-square)](https://cdn.jsdelivr.net/npm/super-media-element/+esm)
A custom element that helps save alienated player API's to bring back their true inner [HTMLMediaElement API](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement), or to extend a native media element like `<audio>` or `<video>`.

@@ -4,0 +7,0 @@

@@ -11,2 +11,34 @@ /**

// The onevent like props are weirdly set on the HTMLElement prototype with other
// generic events making it impossible to pick these specific to HTMLMediaElement.
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',
];
const styles = `

@@ -41,49 +73,34 @@ :host {

export const SuperMediaMixin = (superclass, { tag, is }) => {
// Can't check typeof directly on element prototypes without
// throwing Illegal Invocation errors, so creating an element
// to check on instead.
const nativeElTest = document.createElement(tag, { is });
const nativeElProps = getNativeElProps(nativeElTest);
// Most of the media events are set on the HTMLElement prototype.
const AllEvents = [
...nativeElProps,
...Object.getOwnPropertyNames(HTMLElement.prototype),
]
.filter((name) => name.startsWith('on'))
.map((name) => name.slice(2));
return class SuperMedia extends superclass {
static template = template;
static Events = Events;
static #isDefined;
// observedAttributes is required to trigger attributeChangedCallback
// for any attributes on the custom element.
// Attributes need to be the lowercase word, e.g. crossorigin, not crossOrigin
static get observedAttributes() {
SuperMedia.#define();
// Instead of manually creating a list of all observed attributes,
// observe any getter/setter prop name (lowercase)
let attrs = [];
Object.getOwnPropertyNames(this.prototype).forEach((propName) => {
// Non-func properties throw errors because it's not an instance
let isFunc = false;
try {
if (typeof this.prototype[propName] === 'function') isFunc = true;
} catch (e) {
//
}
// Exclude functions and constants
if (!isFunc && propName !== propName.toUpperCase()) {
attrs.push(propName.toLowerCase());
}
});
// Include any attributes from the super class (recursive)
const supAttrs = Object.getPrototypeOf(this).observedAttributes;
// Include any attributes from the custom built-in.
const natAttrs = Object.getPrototypeOf(nativeElTest).observedAttributes;
return [...(natAttrs ?? []), ...attrs, ...(supAttrs ?? [])];
const attrs = [
...(natAttrs ?? []),
'autopictureinpicture',
'disablepictureinpicture',
'disableremoteplayback',
'autoplay',
'controls',
'controlslist',
'crossorigin',
'loop',
'muted',
'playsinline',
'poster',
'preload',
'src',
];
return attrs;
}

@@ -120,7 +137,3 @@

this.#init();
return (
this.get?.(prop) ??
this.nativeEl?.[prop] ??
this.#standinEl[prop]
);
return this.get?.(prop) ?? this.nativeEl?.[prop] ?? this.#standinEl[prop];
},

@@ -191,27 +204,2 @@ };

#initStandinEl() {
const dummyEl = document.createElement(tag, { is });
[...this.attributes].forEach(({ name, value }) => {
dummyEl.setAttribute(name, value);
});
this.#standinEl = {};
getNativeElProps(dummyEl).forEach((name) => {
this.#standinEl[name] = dummyEl[name];
});
// unload dummy video element
dummyEl.removeAttribute('src');
dummyEl.load();
}
async #initNativeEl() {
if (this.loadComplete && !this.isLoaded) await this.loadComplete;
// If there is no nativeEl by now, create it our bloody selves.
if (!this.nativeEl) {
this.shadowRoot.append(document.createElement(tag, { is }));
}
}
async #init() {

@@ -251,96 +239,59 @@ if (this.#isInit) return;

// This makes it possible to add event listeners before the element is upgraded.
AllEvents.forEach((type) => {
this.shadowRoot.addEventListener?.(
type,
(evt) => {
if (evt.target !== this.nativeEl) {
return;
}
// Filter out non-media events.
if (
!['Event', 'CustomEvent', 'PictureInPictureEvent'].includes(
evt.constructor.name
)
) {
return;
}
this.dispatchEvent(
new CustomEvent(evt.type, { detail: evt.detail })
);
},
true
);
Events.forEach((type) => {
this.shadowRoot.addEventListener?.(type, (evt) => {
if (evt.target !== this.nativeEl) return;
this.dispatchEvent(new CustomEvent(evt.type, { detail: evt.detail }));
}, true);
});
}
// Initialize all the attribute properties
// This is required before attributeChangedCallback is called after construction
// so the initial state of all the attributes are forwarded to the native element.
// Don't call attributeChangedCallback directly here because the extending class
// could have overridden attributeChangedCallback leading to unexpected results.
[...this.attributes].forEach((attrNode) => {
this.#forwardAttribute(attrNode.name, null, attrNode.value);
#initStandinEl() {
const dummyEl = document.createElement(tag, { is });
[...this.attributes].forEach(({ name, value }) => {
dummyEl.setAttribute(name, value);
});
// Neither Chrome or Firefox support setting the muted attribute
// after using document.createElement.
// One way to get around this would be to build the native tag as a string.
// But just fixing it manually for now.
// Apparently this may also be an issue with <input checked> for buttons
this.#standinEl = {};
getNativeElProps(dummyEl).forEach((name) => {
this.#standinEl[name] = dummyEl[name];
});
// unload dummy video element
dummyEl.removeAttribute('src');
dummyEl.load();
}
async #initNativeEl() {
if (this.loadComplete && !this.isLoaded) await this.loadComplete;
if (this.nativeEl.defaultMuted) {
this.muted = true;
// If there is no nativeEl by now, create it our bloody selves.
if (!this.nativeEl) {
// Neither Chrome or Firefox support setting the muted attribute
// after using document.createElement.
// Get around this by building the native tag as a string.
const muted = this.hasAttribute('muted') ? ' muted' : '';
const tpl = document.createElement('template');
tpl.innerHTML = `<${tag}${is ? ` is="${is}"` : ''}${muted}></${tag}>`;
this.shadowRoot.append(tpl.content);
}
}
async attributeChangedCallback(attrName, oldValue, newValue) {
attributeChangedCallback(attrName, oldValue, newValue) {
// Initialize right after construction when the attributes become available.
if (!this.#isInit) {
await this.#init();
}
this.#init();
this.#forwardAttribute(attrName, oldValue, newValue);
}
// We need to handle sub-class custom attributes differently from
// attrs meant to be passed to the internal native el.
async #forwardAttribute(attrName, oldValue, newValue) {
if (this.loadComplete && !this.isLoaded) await this.loadComplete;
// Find the matching prop for custom attributes
const ownProps = Object.getOwnPropertyNames(Object.getPrototypeOf(this));
const propName = ownProps.find(
(name) => name.toLowerCase() === attrName.toLowerCase()
);
// Check if this is the original custom native element or a subclass
const isBaseElement =
Object.getPrototypeOf(this.constructor).name === 'HTMLElement';
// If this is a subclass custom attribute we want to set the
// matching property on the subclass
if (propName && !isBaseElement) {
// Boolean props should never start as null
if (typeof this[propName] == 'boolean') {
// null is returned when attributes are removed i.e. boolean attrs
if (newValue === null) {
this[propName] = false;
} else {
// The new value might be an empty string, which is still true
// for boolean attributes
this[propName] = true;
}
} else {
this[propName] = newValue;
}
if (newValue === null) {
this.nativeEl.removeAttribute?.(attrName);
} else {
// When this is the original Custom Element, or the subclass doesn't
// have a matching prop, pass it through.
if (newValue === null) {
this.nativeEl.removeAttribute?.(attrName);
} else {
// Ignore a few that don't need to be passed through just in case
// it creates unexpected behavior.
if (!['id', 'class'].includes(attrName)) {
this.nativeEl.setAttribute?.(attrName, newValue);
}
// Ignore a few that don't need to be passed through just in case
// it creates unexpected behavior.
if (!['id', 'class'].includes(attrName)) {
this.nativeEl.setAttribute?.(attrName, newValue);
}

@@ -347,0 +298,0 @@ }

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc